"""Subscription service — plan queries and Stripe plan updates."""
from __future__ import annotations

import logging
from typing import Optional
from uuid import UUID

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from src.core.config import settings
from src.core.constants import SubscriptionPlan, PLAN_PRICES_CAD, TRIAL_DAYS
from src.core.exceptions import NotFoundError, ValidationError

logger = logging.getLogger(__name__)

_PLAN_NAMES = {
    SubscriptionPlan.STARTER: "Starter",
    SubscriptionPlan.PROFESSIONAL: "Professional",
    SubscriptionPlan.ENTERPRISE: "Enterprise",
}

_STRIPE_PRICE_MAP = {
    SubscriptionPlan.STARTER: "STRIPE_PRICE_STARTER",
    SubscriptionPlan.PROFESSIONAL: "STRIPE_PRICE_PROFESSIONAL",
    SubscriptionPlan.ENTERPRISE: "STRIPE_PRICE_ENTERPRISE",
}


def _get_stripe_price_id(plan: SubscriptionPlan) -> str:
    attr = _STRIPE_PRICE_MAP[plan]
    return getattr(settings, attr, "")


class SubscriptionService:

    async def get_subscription(
        self,
        db: AsyncSession,
        tenant_id: UUID | str,
    ) -> dict:
        from src.apps.tenants.models.account import Account
        from src.apps.tenants.models.subscription import Subscription

        account = (
            await db.execute(select(Account).where(Account.id == tenant_id))
        ).scalar_one_or_none()
        if not account:
            raise NotFoundError("Account not found")

        sub = (
            await db.execute(
                select(Subscription)
                .where(Subscription.account_id == account.id)
                .order_by(Subscription.created_at.desc())
                .limit(1)
            )
        ).scalar_one_or_none()

        plan_slug = account.plan or SubscriptionPlan.STARTER.value
        try:
            plan_enum = SubscriptionPlan(plan_slug)
        except ValueError:
            plan_enum = SubscriptionPlan.STARTER

        price_monthly = int(PLAN_PRICES_CAD.get(plan_enum, 149))
        plan_name = _PLAN_NAMES.get(plan_enum, plan_slug.capitalize())

        next_billing_date: Optional[str] = None
        if sub and sub.current_period_end:
            next_billing_date = sub.current_period_end.date().isoformat()
        elif account.onboarding_completed_at:
            # No Stripe subscription yet — derive first payment date from trial period
            from datetime import timedelta
            trial_end = account.onboarding_completed_at + timedelta(days=TRIAL_DAYS)
            next_billing_date = trial_end.date().isoformat()

        return {
            "plan_name": plan_name,
            "plan_slug": plan_slug,
            "price_monthly": price_monthly,
            "currency": "CAD",
            "status": sub.status if sub else account.status,
            "next_billing_date": next_billing_date,
            "org_name": account.organization_name,
            "contact_name": "",
            "contact_email": account.contact_email,
        }

    async def update_plan(
        self,
        db: AsyncSession,
        tenant_id: UUID | str,
        new_plan: str,
    ) -> dict:
        from src.apps.tenants.models.account import Account
        from src.apps.tenants.models.subscription import Subscription

        try:
            plan_enum = SubscriptionPlan(new_plan)
        except ValueError:
            raise ValidationError(f"Invalid plan: {new_plan}. Must be one of: starter, professional, enterprise")

        account = (
            await db.execute(select(Account).where(Account.id == tenant_id))
        ).scalar_one_or_none()
        if not account:
            raise NotFoundError("Account not found")

        sub = (
            await db.execute(
                select(Subscription)
                .where(Subscription.account_id == account.id)
                .order_by(Subscription.created_at.desc())
                .limit(1)
            )
        ).scalar_one_or_none()

        # Call Stripe if subscription exists
        if sub and sub.stripe_subscription_id and settings.STRIPE_SECRET_KEY:
            try:
                import stripe
                stripe.api_key = settings.STRIPE_SECRET_KEY

                price_id = _get_stripe_price_id(plan_enum)
                if not price_id:
                    logger.warning("[billing] No Stripe price ID configured for plan %s", new_plan)
                else:
                    stripe_sub = stripe.Subscription.retrieve(sub.stripe_subscription_id)
                    item_id = stripe_sub["items"]["data"][0]["id"]
                    stripe.Subscription.modify(
                        sub.stripe_subscription_id,
                        items=[{"id": item_id, "price": price_id}],
                        proration_behavior="create_prorations",
                    )
                    logger.info("[billing] Stripe subscription updated to plan=%s", new_plan)
            except Exception as exc:
                logger.error("[billing] Stripe subscription update failed: %s", exc)
                raise ValidationError(f"Stripe update failed: {exc}")

        # Update DB
        account.plan = plan_enum.value
        if sub:
            sub.plan = plan_enum.value
            sub.amount_cad = PLAN_PRICES_CAD[plan_enum]

        await db.flush()
        return await self.get_subscription(db, tenant_id)
