"""
Admin endpoint for synchronising tier changes from Stripe/Clerk.

The web application (Next.js) calls this endpoint after updating Clerk metadata
so that the FastAPI backend maintains the same subscription tier and status.
"""

from __future__ import annotations

import logging
from datetime import datetime
from typing import Literal, Optional

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

from ... import settings as saas_settings
from ...db import get_session
from ...middleware.quota_checker import TIER_LIMITS
from ...models import UsageQuotaModel
from ...services.usage_gateway import UsageGateway

log = logging.getLogger("fmatch.api.sync_tier")

router = APIRouter(prefix="/api/v1/admin", tags=["admin-internal"])


class TierSyncPayload(BaseModel):
    """Payload for tier synchronisation from Stripe webhooks or admin tooling."""

    clerk_user_id: str
    tier: Literal["free", "pro", "scale", "unleashed"]
    status: str  # active, canceled, past_due, trialing, etc.
    monthly_enrichments: Optional[int] = None
    monthly_match_limit: Optional[int] = None
    period_start: Optional[str] = None
    period_end: Optional[str] = None
    source: Optional[str] = "webhook"
    force_reset: Optional[bool] = (
        False  # Allow admin scripts to bypass downgrade protection
    )
    addons: Optional[list[str]] = None

    @root_validator(pre=True)
    def _normalise_aliases(cls, values: dict[str, object]) -> dict[str, object]:
        """Allow camelCase aliases used by the web team."""
        if "monthlyEnrichmentCredits" in values and "monthly_enrichments" not in values:
            values["monthly_enrichments"] = values["monthlyEnrichmentCredits"]
        if "monthlyMatchLimit" in values and "monthly_match_limit" not in values:
            values["monthly_match_limit"] = values["monthlyMatchLimit"]
        if "source" not in values and "event_source" in values:
            values["source"] = values["event_source"]
        return values


async def require_webhook_secret(
    authorization: str = Header(..., alias="Authorization"),
) -> str:
    """
    Verify webhook secret for internal service-to-service calls.

    Expected format: "Bearer <WEBHOOK_SECRET>"
    """

    if not authorization.startswith("Bearer "):
        raise HTTPException(
            status_code=401, detail="Invalid authorization header format"
        )

    token = authorization.replace("Bearer ", "")

    # Development escape hatch to simplify local testing.
    if saas_settings.ENV.lower() in ("dev", "development") and token == "dev-token":
        log.info("[sync-tier] Allowing dev-token in development")
        return token

    webhook_secret = saas_settings.WEBHOOK_SECRET
    if not webhook_secret:
        log.error("[sync-tier] WEBHOOK_SECRET not configured!")
        raise HTTPException(
            status_code=500, detail="Webhook authentication not configured"
        )

    if token != webhook_secret:
        log.warning("[sync-tier] Invalid webhook secret provided")
        raise HTTPException(status_code=401, detail="Invalid webhook secret")

    return token


def _resolve_entitlements(payload: TierSyncPayload) -> tuple[int, int]:
    """Determine entitlement values using payload overrides or tier defaults."""
    tier_limits = TIER_LIMITS.get(payload.tier, TIER_LIMITS["free"])
    monthly_rows = payload.monthly_match_limit or tier_limits["monthly_rows"]
    monthly_enrichments = (
        payload.monthly_enrichments or tier_limits["enrichment_credits"]
    )
    return monthly_rows, monthly_enrichments


@router.post("/sync-tier")
async def sync_tier_from_stripe(
    payload: TierSyncPayload,
    _auth: str = Depends(require_webhook_secret),
    db: AsyncSession = Depends(get_session),
) -> dict:
    """
    Update UsageQuotaModel to reflect the tier coming from Stripe/Clerk.

    Flow:
        1. Stripe fires webhook to Next.js
        2. Next.js updates PostgreSQL & Clerk metadata
        3. Next.js calls this endpoint with the same tier information
    """

    tier_limits = TIER_LIMITS.get(payload.tier)
    if not tier_limits:
        raise HTTPException(
            status_code=400,
            detail=f"Invalid tier: {payload.tier}. Must be one of: {list(TIER_LIMITS.keys())}",
        )

    monthly_rows, monthly_enrichments = _resolve_entitlements(payload)

    enrichments_str = (
        f", enrichments={monthly_enrichments}"
        if monthly_enrichments is not None
        else ""
    )
    log.info(
        "[sync-tier] Received tier sync for user %s: tier=%s, status=%s%s, source=%s",
        payload.clerk_user_id,
        payload.tier,
        payload.status,
        enrichments_str,
        payload.source,
    )

    stmt = select(UsageQuotaModel).where(
        UsageQuotaModel.account_id == payload.clerk_user_id
    )
    result = await db.execute(stmt)
    quota = result.scalar_one_or_none()

    now = datetime.utcnow()

    if not quota:
        quota = UsageQuotaModel(
            account_id=payload.clerk_user_id,
            tier=payload.tier,
            monthly_rows_used=0,
            enrichment_credits_used=0,
            subscription_status=payload.status or "unknown",
            tier_synced_at=now,
            tier_sync_source=payload.source or "webhook",
            monthly_enrichment_entitlement=monthly_enrichments,
            monthly_match_entitlement=monthly_rows,
        )
        db.add(quota)
        await db.commit()
        await db.refresh(quota)

        try:
            gateway = UsageGateway(db)
            await gateway.sync_from_stripe(
                account_id=payload.clerk_user_id,
                tier=payload.tier,
                addons=payload.addons or [],
                source=payload.source or "webhook",
                monthly_row_limit=monthly_rows,
            )
        except Exception as exc:  # noqa: BLE001 - best effort
            log.warning("[sync-tier] Failed to sync usage gateway: %s", exc)

        return {
            "ok": True,
            "updated": True,
            "message": f"Created new quota record with tier={payload.tier}",
            "account_id": payload.clerk_user_id,
            "tier": quota.tier,
            "status": quota.subscription_status,
            "enrichments": quota.monthly_enrichment_entitlement,
            "monthly_match_limit": quota.monthly_match_entitlement,
            "source": quota.tier_sync_source,
        }

    old_tier = quota.tier

    # Protect against erroneous downgrades from paid to free
    # If current tier is paid (pro/scale) and new tier is free, check if this is legitimate
    if (
        old_tier in ("pro", "scale")
        and payload.tier == "free"
        and not payload.force_reset
    ):
        # Check if there's an active subscription in the database
        from sqlalchemy import text

        active_sub = await db.execute(
            text("""
                SELECT s.stripe_subscription_id, s.status
                FROM subscription s
                WHERE s.account_id = :aid
                AND s.status IN ('active', 'trialing')
                ORDER BY s.created_at DESC
                LIMIT 1
            """),
            {"aid": payload.clerk_user_id},
        )
        active_subscription = active_sub.first()

        if active_subscription:
            # There's an active subscription - reject the downgrade regardless of status
            log.warning(
                "[sync-tier] Rejecting downgrade from %s to free - active subscription %s exists (status=%s). "
                "If this is legitimate, cancel the subscription first or use force_reset=true. Source: %s",
                old_tier,
                active_subscription[0],
                active_subscription[1],
                payload.source,
            )
            return {
                "ok": True,
                "updated": False,
                "message": f"Rejected downgrade from {old_tier} to free - active subscription exists",
                "account_id": payload.clerk_user_id,
                "old_tier": old_tier,
                "new_tier": old_tier,  # Kept old tier
                "status": quota.subscription_status,
                "enrichments": quota.monthly_enrichment_entitlement,
                "monthly_match_limit": quota.monthly_match_entitlement,
                "source": quota.tier_sync_source,
                "active_subscription": active_subscription[0],
            }

    # Log if force_reset was used
    if payload.force_reset and old_tier in ("pro", "scale") and payload.tier == "free":
        log.info(
            "[sync-tier] Force reset enabled - allowing downgrade from %s to free. Source: %s",
            old_tier,
            payload.source,
        )

    quota.tier = payload.tier
    quota.subscription_status = payload.status or quota.subscription_status or "unknown"
    quota.tier_synced_at = now
    quota.tier_sync_source = payload.source or quota.tier_sync_source or "webhook"
    quota.monthly_enrichment_entitlement = monthly_enrichments
    quota.monthly_match_entitlement = monthly_rows

    await db.commit()
    await db.refresh(quota)

    try:
        gateway = UsageGateway(db)
        await gateway.sync_from_stripe(
            account_id=payload.clerk_user_id,
            tier=payload.tier,
            addons=payload.addons or [],
            source=payload.source or "webhook",
            monthly_row_limit=monthly_rows,
        )
    except Exception as exc:  # noqa: BLE001 - best effort
        log.warning("[sync-tier] Failed to sync usage gateway: %s", exc)

    log.info(
        "[sync-tier] Updated tier for %s: %s -> %s (status=%s, source=%s)",
        payload.clerk_user_id,
        old_tier,
        quota.tier,
        quota.subscription_status,
        quota.tier_sync_source,
    )

    return {
        "ok": True,
        "updated": old_tier != quota.tier
        or quota.subscription_status != payload.status
        or quota.monthly_enrichment_entitlement != monthly_enrichments,
        "message": f"Tier updated from {old_tier} to {quota.tier}",
        "account_id": payload.clerk_user_id,
        "old_tier": old_tier,
        "new_tier": quota.tier,
        "status": quota.subscription_status,
        "enrichments": quota.monthly_enrichment_entitlement,
        "monthly_match_limit": quota.monthly_match_entitlement,
        "source": quota.tier_sync_source,
    }
