from __future__ import annotations

import logging
from datetime import datetime
from typing import Any, Optional

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

from ... import settings as saas_settings
from ...db import get_session
from ...services.gremlin_install_tokens import gremlin_cli_plan_for_state, issue_local_install_token
from ...services.gremlin_license_gateway import (
    apply_gremlin_gateway_projection,
    fetch_gremlin_gateway_license,
    serialize_gremlin_gateway_license,
)
from ...services.gremlin_self_service import (
    resolve_canonical_gremlin_grant,
    serialize_canonical_gremlin_license,
)

log = logging.getLogger(__name__)

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


class GremlinLicenseProjectionPayload(BaseModel):
    account_id: str
    user_id: Optional[str] = None
    clerk_user_id: Optional[str] = None
    primary_email: Optional[str] = None
    product_surface: str = "gremlin"
    grant_type: str
    plan_key: str
    status: str
    source: str
    starts_at: Optional[datetime] = None
    ends_at: Optional[datetime] = None
    attributes: dict[str, Any] = Field(default_factory=dict)


class GremlinCanonicalLookupPayload(BaseModel):
    account_id: Optional[str] = None
    clerk_user_id: Optional[str] = None
    primary_email: Optional[str] = None
    expires_in_hours: int = 72
    single_use: bool = True


async def require_internal_api_key(
    x_internal_api_key: str = Header(..., alias="X-Internal-Api-Key"),
) -> str:
    expected = (saas_settings.LICENSE_MANAGER_INTERNAL_SECRET or "").strip()
    if not expected:
        log.error("[license-manager-internal] LICENSE_MANAGER_INTERNAL_SECRET not configured")
        raise HTTPException(status_code=500, detail="Internal license gateway secret not configured")
    if x_internal_api_key != expected:
        raise HTTPException(status_code=401, detail="Invalid internal API key")
    return x_internal_api_key


@router.post("/gremlin")
async def project_gremlin_license(
    payload: GremlinLicenseProjectionPayload,
    _auth: str = Depends(require_internal_api_key),
    db: AsyncSession = Depends(get_session),
) -> dict[str, Any]:
    if payload.product_surface != "gremlin":
        raise HTTPException(status_code=400, detail="Only gremlin projections are supported")

    try:
        row = await apply_gremlin_gateway_projection(
            db,
            account_id=payload.account_id,
            user_id=payload.user_id,
            clerk_user_id=payload.clerk_user_id,
            primary_email=payload.primary_email,
            grant_type=payload.grant_type,
            plan_key=payload.plan_key,
            status=payload.status,
            source=payload.source,
            starts_at=payload.starts_at,
            ends_at=payload.ends_at,
            attributes=payload.attributes,
        )
    except LookupError as exc:
        raise HTTPException(status_code=404, detail=str(exc)) from exc

    license_payload = serialize_gremlin_gateway_license(row)
    return {
        "ok": True,
        "licenseId": row.account_id,
        "userId": row.user_id,
        "clerkUserId": row.clerk_user_id,
        "primaryEmail": row.primary_email,
        "license": license_payload,
    }


@router.get("/gremlin")
async def get_gremlin_license_projection(
    account_id: Optional[str] = None,
    clerk_user_id: Optional[str] = None,
    primary_email: Optional[str] = None,
    _auth: str = Depends(require_internal_api_key),
    db: AsyncSession = Depends(get_session),
) -> dict[str, Any]:
    if not (account_id or clerk_user_id or primary_email):
        raise HTTPException(
            status_code=400,
            detail="account_id, clerk_user_id, or primary_email is required",
        )

    row = await fetch_gremlin_gateway_license(
        db,
        account_id=account_id,
        clerk_user_id=clerk_user_id,
        primary_email=primary_email,
    )
    if row is None:
        raise HTTPException(status_code=404, detail="Gremlin gateway license not found")

    return {
        "ok": True,
        "licenseId": row.account_id,
        "userId": row.user_id,
        "clerkUserId": row.clerk_user_id,
        "primaryEmail": row.primary_email,
        "license": serialize_gremlin_gateway_license(row),
    }


@router.get("/gremlin/canonical")
async def get_canonical_gremlin_license(
    account_id: Optional[str] = None,
    clerk_user_id: Optional[str] = None,
    primary_email: Optional[str] = None,
    _auth: str = Depends(require_internal_api_key),
    db: AsyncSession = Depends(get_session),
) -> dict[str, Any]:
    if not (account_id or clerk_user_id or primary_email):
        raise HTTPException(
            status_code=400,
            detail="account_id, clerk_user_id, or primary_email is required",
        )

    canonical_account_id, grant = await resolve_canonical_gremlin_grant(
        db,
        account_id=account_id,
        clerk_user_id=clerk_user_id,
        primary_email=primary_email,
    )
    if grant is None:
        raise HTTPException(status_code=404, detail="Canonical Gremlin grant not found")

    return {
        "ok": True,
        "accountId": canonical_account_id or grant.account_id,
        "license": serialize_canonical_gremlin_license(grant),
    }


@router.post("/gremlin/install-token")
async def issue_canonical_gremlin_install_token(
    payload: GremlinCanonicalLookupPayload,
    _auth: str = Depends(require_internal_api_key),
    db: AsyncSession = Depends(get_session),
) -> dict[str, Any]:
    if not (payload.account_id or payload.clerk_user_id or payload.primary_email):
        raise HTTPException(
            status_code=400,
            detail="account_id, clerk_user_id, or primary_email is required",
        )

    canonical_account_id, grant = await resolve_canonical_gremlin_grant(
        db,
        account_id=payload.account_id,
        clerk_user_id=payload.clerk_user_id,
        primary_email=payload.primary_email,
    )
    if grant is None:
        raise HTTPException(status_code=404, detail="Canonical Gremlin grant not found")

    license_payload = serialize_canonical_gremlin_license(grant)
    if str(license_payload.get("effectiveTier") or "local_free") == "local_free":
        raise HTTPException(status_code=403, detail="No active Gremlin access found")

    token_payload = issue_local_install_token(
        account_id=canonical_account_id or grant.account_id,
        clerk_user_id=grant.clerk_user_id,
        primary_email=grant.primary_email,
        product_surface="gremlin",
        expires_in_hours=max(1, int(payload.expires_in_hours)),
        single_use=bool(payload.single_use),
    )
    token = str(token_payload["token"])

    return {
        "ok": True,
        "accountId": canonical_account_id or grant.account_id,
        "license": license_payload,
        "token": token,
        "expiresIn": max(1, int(payload.expires_in_hours)) * 3600,
        "expiresAt": token_payload["token_expires_at"],
        "tier": license_payload.get("effectiveTier"),
        "plan": gremlin_cli_plan_for_state(grant.plan_key),
        "command": f"g-gremlin license install {token}",
        "singleUse": bool(token_payload["single_use"]),
    }
