"""Cloudflare for SaaS — Custom Hostnames API client.

Handles zone-level custom hostname lifecycle for customer domains:
  - Create: register a hostname + begin TLS provisioning
  - Get: poll ssl.status / status for cert readiness
  - Delete: remove hostname when customer removes their domain

Requires:
  - CLOUDFLARE_API_TOKEN with Zone:Edit permission
  - CLOUDFLARE_ZONE_ID — the P2HS apex zone (e.g. the zone that owns sites.p2hs.com)
  - CF for SaaS enabled on that zone (dashboard → SSL/TLS → Custom Hostnames)
  - A fallback origin configured; HOSTING_CNAME_TARGET must point at it
    (e.g. custom.sites.p2hs.com proxied through this zone)
"""
from __future__ import annotations

from typing import TYPE_CHECKING, Any

import httpx
import structlog

from src.config import Settings, get_settings

if TYPE_CHECKING:
    from src.domain.cloud_infra.service import CfCredentials

logger = structlog.get_logger()

CF_API = "https://api.cloudflare.com/client/v4"

# CF ssl.status values that mean the cert is live and serving
_TLS_ACTIVE_STATUSES = {"active"}

# CF ssl.status values that mean something is wrong / unrecoverable without action
_TLS_ERROR_STATUSES = {"validation_timed_out", "deleted"}


class CFSaaSError(RuntimeError):
    pass


class CFSaaSClient:
    """Thin wrapper around the CF Custom Hostnames zone API.

    Accepts either a ``CfCredentials`` object (preferred — resolved per workspace
    or for the primary account) or falls back to ``settings`` env vars for
    backward compatibility.
    """

    def __init__(
        self,
        settings: Settings | None = None,
        *,
        credentials: CfCredentials | None = None,
    ):
        self._s = settings or get_settings()
        self._creds = credentials

    @property
    def _zone_id(self) -> str:
        if self._creds:
            z = (self._creds.zone_id or "").strip()
            if not z:
                raise CFSaaSError("CF account has no zone_id configured")
            return z
        z = (self._s.cloudflare_zone_id or "").strip()
        if not z:
            raise CFSaaSError(
                "CLOUDFLARE_ZONE_ID is not configured. "
                "Set it to the zone that owns your hosting apex (e.g. p2hs.com)."
            )
        return z

    @property
    def _token(self) -> str:
        if self._creds:
            return self._creds.api_token
        # Prefer the dedicated zone token; fall back to the account token.
        zone_token = (self._s.cloudflare_zone_api_token or "").strip()
        return zone_token or str(self._s.cloudflare_api_token).strip()

    def _headers(self) -> dict[str, str]:
        return {"Authorization": f"Bearer {self._token}", "Content-Type": "application/json"}

    def _base(self) -> str:
        return f"{CF_API}/zones/{self._zone_id}/custom_hostnames"

    async def _request(self, method: str, url: str, **kwargs: Any) -> dict[str, Any]:
        async with httpx.AsyncClient(timeout=30.0) as client:
            resp = await client.request(method.upper(), url, headers=self._headers(), **kwargs)
        if resp.status_code == 404:
            return {}
        if not resp.is_success:
            try:
                detail = resp.json()
            except Exception:
                detail = resp.text
            raise CFSaaSError(f"CF SaaS API {method.upper()} {url} → {resp.status_code}: {detail}")
        if not resp.content:
            return {}
        payload = resp.json()
        return payload.get("result") or payload

    async def create(self, hostname: str) -> dict[str, Any]:
        """Register a custom hostname and begin DV TLS provisioning.

        Returns the CF custom hostname object including its ``id``.
        The ``ssl.status`` will be ``pending_validation`` initially.
        """
        result = await self._request(
            "POST",
            self._base(),
            json={
                "hostname": hostname,
                "ssl": {
                    "method": "http",
                    "type": "dv",
                    "settings": {
                        "min_tls_version": "1.2",
                        "always_use_https": True,
                    },
                },
            },
        )
        logger.info("cf_saas_hostname_created", hostname=hostname, cf_id=result.get("id"))
        return result

    async def get(self, cf_hostname_id: str) -> dict[str, Any]:
        """Fetch current status for a registered custom hostname."""
        return await self._request("GET", f"{self._base()}/{cf_hostname_id}")

    async def find_by_hostname(self, hostname: str) -> dict[str, Any] | None:
        """Look up an existing custom hostname registration by name."""
        result = await self._request("GET", self._base(), params={"hostname": hostname})
        if isinstance(result, list):
            return result[0] if result else None
        return None

    async def delete(self, cf_hostname_id: str) -> None:
        """Remove a custom hostname from the zone."""
        await self._request("DELETE", f"{self._base()}/{cf_hostname_id}")
        logger.info("cf_saas_hostname_deleted", cf_id=cf_hostname_id)

    @staticmethod
    def parse_status(cf_obj: dict[str, Any]) -> dict[str, Any]:
        """Extract a normalised status summary from a CF custom hostname object.

        Returns:
            hostname_active: bool  — CF is accepting + proxying the hostname
            tls_active:      bool  — cert is issued and live
            tls_status:      str   — raw CF ssl.status value
            hostname_status: str   — raw CF top-level status value
            tls_error:       bool  — provisioning has failed unrecoverably
        """
        ssl = cf_obj.get("ssl") or {}
        ssl_status: str = ssl.get("status") or "unknown"
        hostname_status: str = cf_obj.get("status") or "unknown"
        tls_active = ssl_status in _TLS_ACTIVE_STATUSES
        tls_error = ssl_status in _TLS_ERROR_STATUSES
        hostname_active = hostname_status == "active"
        return {
            "hostname_active": hostname_active,
            "tls_active": tls_active,
            "tls_status": ssl_status,
            "hostname_status": hostname_status,
            "tls_error": tls_error,
        }
