"""POST /v1/nps — NPS score and cancellation feedback collection.

Auth: HMAC-SHA256 signed token in URL query param ``?token=...``.
No login required — links work directly from email.
Token validity: 30 days from issuance.
"""

from __future__ import annotations

import hashlib
import hmac
import logging
from datetime import UTC, datetime
from typing import Any

from fastapi import APIRouter, Request
from fastapi.responses import JSONResponse
from pydantic import BaseModel, Field, StrictInt, StrictStr, model_validator

from backend.shared.firestore import get_db

_log = logging.getLogger(__name__)

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

_VALID_REASONS = frozenset(
    {
        "too_expensive",
        "missing_features",
        "competitor",
        "no_longer_needed",
    }
)


# ---------------------------------------------------------------------------
# Request models
# ---------------------------------------------------------------------------


class NPSScoreBody(BaseModel):
    """NPS score submission (0-10)."""

    score: StrictInt = Field(ge=0, le=10)


class CancellationBody(BaseModel):
    """Cancellation feedback submission."""

    reason: StrictStr

    @model_validator(mode="after")
    def _validate_reason(self) -> CancellationBody:
        if self.reason not in _VALID_REASONS:
            msg = f"reason must be one of: {', '.join(sorted(_VALID_REASONS))}"
            raise ValueError(msg)
        return self


# ---------------------------------------------------------------------------
# Token helpers
# ---------------------------------------------------------------------------


async def _get_nps_signing_key() -> str:
    """Retrieve NPS signing key from Secret Manager."""
    from google.cloud import secretmanager

    client = secretmanager.SecretManagerServiceClient()
    name = "projects/complyform-prod/secrets/nps-signing-key/versions/latest"
    response = client.access_secret_version(request={"name": name})
    return response.payload.data.decode("utf-8")


def _verify_token(token: str, signing_key: str) -> tuple[str, str] | None:
    """Verify HMAC-SHA256 token.  Returns (customer_id, type) or None."""
    parts = token.split("|")
    if len(parts) != 4:  # noqa: PLR2004
        return None

    customer_id, token_type, expires_at, signature = parts

    # Check expiry
    try:
        exp = datetime.fromisoformat(expires_at)
        if exp.tzinfo is None:
            exp = exp.replace(tzinfo=UTC)
        if datetime.now(UTC) > exp:
            return None
    except ValueError, TypeError:
        return None

    # Verify signature
    message = f"{customer_id}|{token_type}|{expires_at}"
    expected = hmac.new(
        signing_key.encode(),
        message.encode(),
        hashlib.sha256,
    ).hexdigest()

    if not hmac.compare_digest(signature, expected):
        return None

    return customer_id, token_type


# ---------------------------------------------------------------------------
# Endpoint
# ---------------------------------------------------------------------------


@router.post("/nps")
async def collect_nps(request: Request) -> JSONResponse:
    """Collect NPS score or cancellation feedback.

    Auth: HMAC-SHA256 signed token in URL query param ``?token=...``.
    Token validity: 30 days.
    Deduplication: same type within 24hr returns 409.
    """
    token = request.query_params.get("token", "")
    if not token:
        return JSONResponse(
            status_code=403,
            content={"status": "error", "message": "Missing token"},
        )

    try:
        signing_key = await _get_nps_signing_key()
    except Exception:
        _log.exception("Failed to get NPS signing key")
        return JSONResponse(
            status_code=500,
            content={"status": "error", "message": "Internal error"},
        )

    result = _verify_token(token, signing_key)
    if result is None:
        return JSONResponse(
            status_code=403,
            content={"status": "error", "message": "Invalid or expired token"},
        )

    customer_id, token_type = result

    # Parse body based on type
    try:
        body = await request.json()
    except Exception:
        return JSONResponse(
            status_code=422,
            content={"status": "error", "message": "Invalid request body"},
        )

    score: int | None = None
    reason: str | None = None

    if token_type == "nps":
        try:
            parsed = NPSScoreBody(**body)
            score = parsed.score
        except Exception as exc:
            return JSONResponse(
                status_code=422,
                content={"status": "error", "message": str(exc)},
            )
    elif token_type == "cancellation":
        try:
            parsed_cancel = CancellationBody(**body)
            reason = parsed_cancel.reason
        except Exception as exc:
            return JSONResponse(
                status_code=422,
                content={"status": "error", "message": str(exc)},
            )
    else:
        return JSONResponse(
            status_code=422,
            content={
                "status": "error",
                "message": f"Unknown feedback type: {token_type}",
            },
        )

    db = get_db()
    now = datetime.now(UTC)

    # Deduplication: check for same type within 24hr
    feedback_ref = db.collection("customer_feedback").document(customer_id)
    cutoff = now - __import__("datetime").timedelta(hours=24)

    sub_ref = feedback_ref.collection("submissions")
    query = sub_ref.where("type", "==", token_type).where(
        "submitted_at",
        ">=",
        cutoff.isoformat(),
    )
    async for _ in query.stream():
        return JSONResponse(
            status_code=409,
            content={
                "status": "error",
                "message": "Feedback already submitted recently",
            },
        )

    # Get customer tier at time of feedback
    customer_snap = await db.collection("customers").document(customer_id).get()
    tier_at_time = "unknown"
    if customer_snap.exists:
        cdata: dict[str, Any] = customer_snap.to_dict() or {}
        tier_at_time = cdata.get("tier", "unknown")

    # Write feedback
    timestamp_str = now.isoformat()
    await sub_ref.document(timestamp_str).set(
        {
            "customer_id": customer_id,
            "type": token_type,
            "score": score,
            "reason": reason,
            "tier_at_time": tier_at_time,
            "submitted_at": timestamp_str,
        }
    )

    return JSONResponse(
        status_code=200,
        content={"status": "ok", "message": "Thank you for your feedback"},
    )
