from __future__ import annotations

import logging
import os
from typing import Optional

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

from ... import settings as saas_settings
from ...auth_core import get_current_identity
from ...db import get_session
from ...models import Account

log = logging.getLogger(__name__)

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


class PortalRequest(BaseModel):
    return_url: Optional[str] = None


class PortalResponse(BaseModel):
    url: str
    portal_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() -> str:
    base = (
        saas_settings.APP_URL
        or saas_settings.FRONTEND_ORIGIN
        or "http://localhost:3000"
    ).rstrip("/")
    return f"{base}/dashboard"


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(
                "[PORTAL] 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("/portal", response_model=PortalResponse)
async def create_portal_session(
    payload: PortalRequest,
    identity: dict = Depends(get_current_identity),
    db: AsyncSession = Depends(get_session),
):
    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)

    return_url = payload.return_url or _default_return_url()
    session = stripe.billing_portal.Session.create(
        customer=customer_id, return_url=return_url
    )

    return PortalResponse(url=session.url, portal_url=session.url)
