from __future__ import annotations

from datetime import date
from uuid import UUID

from fastapi import APIRouter, Depends, Query, Request
from sqlalchemy.ext.asyncio import AsyncSession

from src.config import get_settings
from src.core.app_url import resolve_checkout_redirect_urls
from src.core.database import get_db
from src.core.exceptions import ForbiddenError
from src.domain.admin.repository import PlanPriceVersionRepository, SubscriptionPlanRepository
from src.domain.auth.models import User
from src.domain.auth.router import get_current_user
from src.domain.billing.catalog_service import BillingCatalogService
from src.domain.billing.repository import (
    InvoiceRepository,
    StripeCustomerRepository,
    StripeEventRepository,
    SubscriptionRepository,
    UsageCreditPackageRepository,
    UsageCreditPurchaseRepository,
    UsageLedgerRepository,
    WorkspaceCreditLedgerRepository,
    WorkspaceUsageAllowanceRepository,
)
from src.domain.billing.schemas import CheckoutSessionResponse
from src.domain.billing.service import BillingService
from src.domain.cloud_credits.schemas import (
    CloudCreditCheckoutRequest,
    CloudCreditPackagePublic,
    CloudWalletSnapshot,
    OwnerBypassResponse,
    WorkspaceWalletsResponse,
)
from src.domain.cloud_credits.service import CloudCreditService
from src.domain.hosting.public_serving_gate import HostingPublicServingGate
from src.domain.usage.repository import WorkspaceUsageSnapshotRepository
from src.domain.workspaces.facades import WorkspaceFacadeImpl
from src.domain.workspaces.repository import MembershipRepository, WorkspaceRepository
from src.integrations.stripe_client import StripeClientImpl

router = APIRouter(prefix="/api/billing", tags=["cloud-credits"])


def _stripe():
    settings = get_settings()
    return StripeClientImpl() if settings.stripe_secret_key else None


def _billing_service(db: AsyncSession) -> BillingService:
    return BillingService(
        customer_repo=StripeCustomerRepository(db),
        subscription_repo=SubscriptionRepository(db),
        invoice_repo=InvoiceRepository(db),
        event_repo=StripeEventRepository(db),
        usage_repo=UsageLedgerRepository(db),
        purchase_repo=UsageCreditPurchaseRepository(db),
        ledger_repo=WorkspaceCreditLedgerRepository(db),
        catalog=BillingCatalogService(
            plan_repo=SubscriptionPlanRepository(db),
            package_repo=UsageCreditPackageRepository(db),
            price_version_repo=PlanPriceVersionRepository(db),
        ),
        stripe_client=_stripe(),
        workspace_repo=WorkspaceRepository(db),
        allowance_repo=WorkspaceUsageAllowanceRepository(db),
    )


def _cloud_credits(db: AsyncSession) -> CloudCreditService:
    billing = _billing_service(db)
    return CloudCreditService(
        db=db,
        stripe_client=_stripe(),
        ensure_customer=billing.ensure_customer,
        serving_gate=HostingPublicServingGate(),
    )


def _codegen_alert(used_pct: float | None) -> str | None:
    if used_pct is None:
        return None
    if used_pct >= 100:
        return "exhausted"
    if used_pct >= 80:
        return "warning"
    return None


@router.get("/wallets", response_model=WorkspaceWalletsResponse)
async def get_workspace_wallets(
    workspace_id: UUID = Query(...),
    user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
) -> WorkspaceWalletsResponse:
    facade = WorkspaceFacadeImpl(WorkspaceRepository(db), MembershipRepository(db))
    if not await facade.check_permission(workspace_id, user.id, "invoices.read"):
        raise ForbiddenError("Workspace billing access required")

    from src.domain.billing.balance_service import WorkspaceUsageBalanceService

    balance_svc = WorkspaceUsageBalanceService(
        subscription_repo=SubscriptionRepository(db),
        plan_repo=SubscriptionPlanRepository(db),
        allowance_repo=WorkspaceUsageAllowanceRepository(db),
        ledger_repo=WorkspaceCreditLedgerRepository(db),
    )
    codegen = await balance_svc.get_balance(workspace_id)
    allowance = await WorkspaceUsageAllowanceRepository(db).get_current_for_workspace(workspace_id)
    included_units = allowance.included_units if allowance else 0
    consumed_units = allowance.consumed_units if allowance else 0
    pool = included_units + codegen.purchased_topup_remaining + consumed_units
    used_pct = round((consumed_units / pool) * 100, 2) if pool > 0 else None
    packages = await UsageCreditPackageRepository(db).list_all(active_only=True)

    sub = await SubscriptionRepository(db).get_by_workspace(workspace_id)
    period_start = sub.current_period_start.date() if sub and sub.current_period_start else date.today().replace(day=1)
    snaps = await WorkspaceUsageSnapshotRepository(db).list_for_workspace(
        workspace_id, from_date=period_start, to_date=date.today(), limit=62
    )
    metrics = {
        "hosted_app_requests": 0.0,
        "hosted_app_egress_bytes": 0.0,
        "cpu_minutes": 0.0,
        "db_reads": 0.0,
        "db_writes": 0.0,
        "storage_gb": 0.0,
        "job_executions": 0.0,
        "ai_embed_tokens": 0.0,
        "ai_completion_tokens": 0.0,
        "ai_searches": 0.0,
        "ext_queries": 0.0,
    }
    for snap in snaps:
        for key in metrics:
            metrics[key] += float(getattr(snap, key, 0) or 0)

    cloud = await _cloud_credits(db).snapshot(
        workspace_id,
        period_start=sub.current_period_start if sub else None,
        period_metrics=metrics,
    )
    return WorkspaceWalletsResponse(
        workspace_id=workspace_id,
        codegen={
            "included_remaining": codegen.included_allowance_remaining,
            "topup_remaining": codegen.purchased_topup_remaining,
            "total_available": codegen.total_available,
            "included_units": included_units,
            "consumed_units": consumed_units,
            "used_pct": used_pct,
            "alert_level": _codegen_alert(used_pct),
            "has_active_subscription": codegen.has_active_subscription,
            "plan_slug": codegen.plan_slug,
            "packages": [
                {
                    "slug": p.slug,
                    "name": p.name,
                    "description": p.description,
                    "units": p.units,
                    "price_minor": p.price_minor,
                    "currency": p.currency,
                }
                for p in packages
            ],
        },
        cloud=CloudWalletSnapshot.model_validate(cloud),
    )


@router.post("/checkout-session/cloud-topup", response_model=CheckoutSessionResponse)
async def create_cloud_topup_checkout(
    request: Request,
    body: CloudCreditCheckoutRequest,
    workspace_id: UUID = Query(...),
    user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
) -> CheckoutSessionResponse:
    facade = WorkspaceFacadeImpl(WorkspaceRepository(db), MembershipRepository(db))
    if not await facade.check_permission(workspace_id, user.id, "billing.manage"):
        raise ForbiddenError("Workspace billing access required")
    success_url, cancel_url = resolve_checkout_redirect_urls(request, body.success_url, body.cancel_url)
    summary = await facade.get_workspace_summary(workspace_id)
    result = await _cloud_credits(db).start_checkout(
        workspace_id=workspace_id,
        user_id=user.id,
        email=user.email,
        workspace_name=summary.name if summary else "",
        package_slug=body.package_slug,
        success_url=success_url,
        cancel_url=cancel_url,
        idempotency_key=body.idempotency_key or request.headers.get("Idempotency-Key"),
    )
    return CheckoutSessionResponse(
        checkout_url=result["checkout_url"],
        session_id=result["session_id"],
        workspace_id=result.get("workspace_id"),
    )


@router.post("/cloud/owner-bypass", response_model=OwnerBypassResponse)
async def create_owner_bypass(
    workspace_id: UUID = Query(...),
    user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
) -> OwnerBypassResponse:
    facade = WorkspaceFacadeImpl(WorkspaceRepository(db), MembershipRepository(db))
    if not await facade.check_permission(workspace_id, user.id, "billing.manage"):
        raise ForbiddenError("Workspace billing access required")
    token = await _cloud_credits(db).issue_owner_bypass(workspace_id)
    return OwnerBypassResponse(token=token)


@router.get("/cloud/packages", response_model=list[CloudCreditPackagePublic])
async def list_cloud_packages(
    _: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
) -> list[CloudCreditPackagePublic]:
    packages = await _cloud_credits(db).list_packages()
    return [
        CloudCreditPackagePublic(
            slug=p.slug,
            name=p.name,
            description=p.description,
            credits=p.credits,
            price_minor=p.price_minor,
            currency=p.currency,
            stripe_ready=bool(p.stripe_price_id),
        )
        for p in packages
    ]
