from __future__ import annotations

import logging
import os
from typing import List, Optional, Literal

from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, Field, root_validator
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from ... import settings as saas_settings
from ...auth_core import get_current_identity
from ...config.tier_limits import TIER_CONFIG as ENRICHMENT_CONFIG
from ...db import get_session
from ...models import Account

log = logging.getLogger(__name__)

router = APIRouter(prefix="/api/v1", tags=["billing"])


class CheckoutRequest(BaseModel):
    tier: Literal["pro", "scale", "unleashed"]
    billing: Literal["monthly", "annual", "yearly"] = "monthly"
    addons: Optional[List[str]] = Field(default=None)
    addon: Optional[str] = Field(default=None)
    success_url: Optional[str] = None
    cancel_url: Optional[str] = None

    @root_validator(pre=True)
    def _merge_addons(cls, values: dict) -> dict:
        addons = list(values.get("addons") or [])
        addon = values.get("addon")
        if addon:
            if isinstance(addon, str):
                addons.append(addon)
            elif isinstance(addon, list):
                addons.extend([item for item in addon if item])
        values["addons"] = addons or None
        return values


class CheckoutResponse(BaseModel):
    url: str
    session_id: str
    checkout_url: Optional[str] = None


def _get_stripe_client():
    try:
        import stripe
    except ImportError as exc:  # pragma: no cover - optional dependency
        raise HTTPException(
            status_code=500,
            detail="Stripe library not installed. Run: pip install stripe",
        ) from exc

    stripe_key = os.getenv("STRIPE_SECRET_KEY")
    if not stripe_key:
        raise HTTPException(
            status_code=500, detail="STRIPE_SECRET_KEY not configured"
        )
    stripe.api_key = stripe_key
    return stripe


async def _resolve_account(
    db: AsyncSession, org_id: str, *, email: Optional[str] = None
) -> Account:
    result = await db.execute(select(Account).where(Account.id == org_id))
    account = result.scalar_one_or_none()
    if account:
        return account

    result = await db.execute(
        select(Account).where(Account.clerk_org_id == org_id)
    )
    account = result.scalar_one_or_none()
    if account:
        return account

    account = Account(
        id=org_id,
        clerk_org_id=org_id,
        name=email or org_id,
    )
    db.add(account)
    await db.commit()
    await db.refresh(account)
    return account


def _default_return_url(path: str) -> str:
    base = (
        saas_settings.APP_URL
        or saas_settings.FRONTEND_ORIGIN
        or "http://localhost:3000"
    ).rstrip("/")
    return f"{base}{path}"


def _normalize_billing(billing: str) -> str:
    value = str(billing or "").strip().lower()
    if value in ("annual", "yearly", "year"):
        return "annual"
    return "monthly"


def _build_stripe_metadata(identity: dict, account: Account) -> dict:
    metadata = {
        "orgId": identity.get("org_id"),
        "userId": identity.get("user_id"),
        "accountId": account.id,
    }
    return {k: v for k, v in metadata.items() if v}


async def _ensure_stripe_customer(
    stripe, db: AsyncSession, account: Account, identity: dict
) -> str:
    metadata = _build_stripe_metadata(identity, account)
    if account.stripe_customer_id:
        try:
            stripe.Customer.modify(account.stripe_customer_id, metadata=metadata)
        except Exception as exc:  # noqa: BLE001 - best effort
            log.warning(
                "[CHECKOUT] Failed to update Stripe customer metadata: %s", exc
            )
        return account.stripe_customer_id

    customer = stripe.Customer.create(
        email=identity.get("email"),
        name=account.name,
        metadata=metadata,
    )
    account.stripe_customer_id = customer.id
    db.add(account)
    await db.commit()
    await db.refresh(account)
    return customer.id


@router.post("/checkout", response_model=CheckoutResponse)
async def create_checkout_session(
    payload: CheckoutRequest,
    identity: dict = Depends(get_current_identity),
    db: AsyncSession = Depends(get_session),
):
    if ENRICHMENT_CONFIG is None:
        raise HTTPException(
            status_code=500,
            detail="Pricing configuration not loaded; cannot create checkout session.",
        )

    tier_key = payload.tier
    billing = _normalize_billing(payload.billing)

    try:
        tier_config = ENRICHMENT_CONFIG.get_tier_config(tier_key)
    except Exception as exc:
        raise HTTPException(
            status_code=400, detail=f"Unknown tier: {tier_key}"
        ) from exc

    if not tier_config.stripe:
        raise HTTPException(
            status_code=400,
            detail=f"Tier {tier_key} does not have Stripe pricing configured.",
        )

    price_id = (
        tier_config.stripe.price_id_annual
        if billing == "annual"
        else tier_config.stripe.price_id_monthly
    )

    addons = [addon for addon in (payload.addons or []) if addon]
    addons = sorted({addon.strip().lower() for addon in addons if addon})
    line_items = [{"price": price_id, "quantity": 1}]

    for addon_key in addons:
        try:
            addon_cfg = ENRICHMENT_CONFIG.get_addon(addon_key)
        except Exception as exc:
            raise HTTPException(
                status_code=400, detail=f"Unknown add-on: {addon_key}"
            ) from exc
        if addon_cfg.available_tiers and tier_key not in addon_cfg.available_tiers:
            raise HTTPException(
                status_code=400,
                detail=f"Add-on {addon_key} is not available for tier {tier_key}.",
            )
        stripe_cfg = addon_cfg.stripe
        addon_price_id = (
            stripe_cfg.price_id_annual
            if billing == "annual"
            else stripe_cfg.price_id_monthly
        )
        line_items.append({"price": addon_price_id, "quantity": 1})

    account_id = identity.get("org_id")
    if not account_id:
        raise HTTPException(status_code=400, detail="No organization on token.")

    account = await _resolve_account(db, account_id, email=identity.get("email"))
    stripe = _get_stripe_client()
    customer_id = await _ensure_stripe_customer(stripe, db, account, identity)

    metadata = _build_stripe_metadata(identity, account)
    metadata.update(
        {
            "tier": tier_key,
            "interval": billing,
            "addons": ",".join(addons) if addons else None,
        }
    )
    metadata = {k: v for k, v in metadata.items() if v is not None}

    success_url = payload.success_url or _default_return_url(
        "/dashboard?checkout=success"
    )
    cancel_url = payload.cancel_url or _default_return_url(
        "/pricing?checkout=cancel"
    )

    session = stripe.checkout.Session.create(
        mode="subscription",
        customer=customer_id,
        line_items=line_items,
        allow_promotion_codes=True,
        success_url=success_url,
        cancel_url=cancel_url,
        metadata=metadata,
        subscription_data={"metadata": metadata},
        client_reference_id=str(account.id),
    )

    return CheckoutResponse(
        url=session.url,
        checkout_url=session.url,
        session_id=session.id,
    )
