"""Compile the app-facing port catalog into the W4P routing manifest.

The catalog is the source of truth.  This module deliberately contains no
authorization policy; it only describes where an already-authorized request
executes.  Legacy projects can continue using the old compatibility policy
until their route is certified.
"""
from __future__ import annotations

import json
from pathlib import Path
from typing import Any

CATALOG_PATH = Path(__file__).resolve().parents[2] / "integrations" / "contracts" / "platform-port-catalog.json"


class RoutingContractError(ValueError):
    """The catalog cannot be compiled into an unambiguous routing manifest."""


def _specificity(path: str) -> tuple[int, int]:
    parts = [part for part in path.split("/") if part]
    return (sum(not part.startswith(":") for part in parts), len(parts))


def _route_sort_key(item: dict[str, Any]) -> tuple:
    path = item["public_path"]
    parts = [part for part in path.split("/") if part]
    static, length = _specificity(path)
    splat = sum(part.startswith(":") and part.endswith("*") for part in parts)
    return (-static, -length, splat, item["method"], path)


def compile_routing_manifest(catalog: dict[str, Any] | None = None) -> list[dict[str, Any]]:
    """Return deterministic route records suitable for embedding in the dispatcher.

    Existing catalog rows default to ``cloud`` for backwards compatibility.
    New rows should set ``routing.owner`` explicitly.  The password operation
    is the first intentionally BFF-owned exception because its adapter performs
    delete/recreate orchestration and is not semantically equivalent to the
    Cloud users endpoint.
    """
    if catalog is None:
        catalog = json.loads(CATALOG_PATH.read_text(encoding="utf-8"))
    rows = catalog.get("ports")
    if not isinstance(rows, list):
        raise RoutingContractError("platform catalog has no ports list")

    manifest: list[dict[str, Any]] = []
    seen: set[tuple[str, str]] = set()
    for row in rows:
        if not isinstance(row, dict) or not row.get("port_id"):
            raise RoutingContractError("every port must have a port_id")
        bff = row.get("bff")
        cloud = row.get("cloud")
        if not isinstance(bff, dict):
            continue
        public_path = str(bff.get("path") or "")
        method = str(bff.get("method") or "").upper()
        if not public_path or not method:
            raise RoutingContractError(f"{row['port_id']} has an incomplete bff route")
        key = (method, public_path)
        if key in seen:
            raise RoutingContractError(f"ambiguous route registration: {method} {public_path}")
        seen.add(key)
        routing = row.get("routing") if isinstance(row.get("routing"), dict) else {}
        owner = str(routing.get("owner") or "").strip().lower()
        if not owner:
            owner = "bff" if row["port_id"] == "auth.users_set_password" else "cloud"
        if owner not in {"cloud", "bff"}:
            raise RoutingContractError(f"{row['port_id']} has invalid routing owner {owner!r}")
        if owner == "cloud" and not isinstance(cloud, dict):
            raise RoutingContractError(f"{row['port_id']} cloud-owned route has no cloud contract")
        manifest.append({
            "id": str(row["port_id"]),
            "method": method,
            "public_path": public_path,
            "owner": owner,
            "upstream_path": str((cloud or {}).get("path") or "") if owner == "cloud" else None,
        })
    manifest.sort(key=_route_sort_key)
    return manifest


def compile_application_runtime_manifest(catalog: dict[str, Any] | None = None) -> list[dict[str, Any]]:
    """Route table for the portable application runtime.

    Every catalog port is forwarded to its Cloud path with the caller's own
    credential. Auth class is recorded so hosts can prove they did not upgrade
    a session. This manifest does not decide authorization. Cloud does.
    """
    if catalog is None:
        catalog = json.loads(CATALOG_PATH.read_text(encoding="utf-8"))
    rows = catalog.get("ports")
    if not isinstance(rows, list):
        raise RoutingContractError("platform catalog has no ports list")
    routes: list[dict[str, Any]] = []
    seen: set[tuple[str, str]] = set()
    for row in rows:
        if not isinstance(row, dict) or not row.get("port_id"):
            raise RoutingContractError("every port must have a port_id")
        bff = row.get("bff")
        cloud = row.get("cloud") if isinstance(row.get("cloud"), dict) else {}
        if not isinstance(bff, dict):
            continue
        public_path = str(bff.get("path") or "")
        method = str(bff.get("method") or "").upper()
        if not public_path or not method:
            raise RoutingContractError(f"{row['port_id']} has an incomplete bff route")
        key = (method, public_path)
        if key in seen:
            raise RoutingContractError(f"ambiguous route registration: {method} {public_path}")
        seen.add(key)
        routing = row.get("routing") if isinstance(row.get("routing"), dict) else {}
        if str(routing.get("surface") or "") == "dispatcher":
            continue
        auth = str(bff.get("auth") or "session").strip().lower()
        if auth not in {"public", "session", "staff"}:
            raise RoutingContractError(f"{row['port_id']} has invalid auth class {auth!r}")
        routes.append({
            "id": str(row["port_id"]),
            "method": method,
            "public_path": public_path,
            "auth": auth,
            "upstream_path": str(cloud.get("path") or ""),
        })
    routes.sort(key=_route_sort_key)
    return routes

