"""Component-scoped auth/runtime migration compiler and executor.

This module intentionally does not call the full platform catalog projector.  It
is the narrow bridge from a certified current-head boundary to a disposable or
approved migration tree.
"""

from __future__ import annotations

import hashlib
import json
from dataclasses import dataclass
from pathlib import Path
from typing import Any

from src.domain.codegen.app_sdk import app_sdk_files
from src.domain.codegen.auth_users_adapter import (
    _is_platform_adapter,
    auth_users_adapter_files,
    password_admitted,
    remove_unadmitted_password_routes,
)
from src.domain.codegen.dependency_contract import dependency_contract_manifest
from src.domain.codegen.repo_export import TRACKED_CANONICAL_METADATA


def _sha(value: str | bytes) -> str:
    raw = value if isinstance(value, bytes) else value.encode("utf-8")
    return "sha256:" + hashlib.sha256(raw).hexdigest()


@dataclass(frozen=True)
class BoundedMigrationOperation:
    operation_class: str
    component: str
    contract_version: str
    owned_paths: tuple[str, ...]
    before_fingerprints: dict[str, str | None]
    after_fingerprints: dict[str, str | None]
    authority: str
    verification_requirements: tuple[str, ...]


@dataclass(frozen=True)
class BoundedMigrationPlan:
    project_type: str
    operations: tuple[BoundedMigrationOperation, ...]
    expected_changed_paths: tuple[str, ...]
    target_files: dict[str, str]


def _fingerprint(path: Path) -> str | None:
    if not path.is_file():
        return None
    return _sha(path.read_bytes())


def _managed_runtime_manifest(
    sdk_files: dict[str, str], auth_files: dict[str, str], password: bool,
    existing_managed: dict[str, str] | None = None,
) -> str:
    all_auth_files = {**(existing_managed or {}), **auth_files}
    manifest = json.loads(sdk_files["p2hs/managed-runtime.json"])
    paths = sorted(
        name for name in all_auth_files
        if "p2hs-auth-" in name
        or "p2hs-ports.mjs" in name
        or "p2hs-port-contract.mjs" in name
        or "p2hs-records.mjs" in name
        or name.endswith("/api/admin/users.js")
        or name.endswith("/api/admin/users/[[path]].js")
        or name.endswith("/api/admin/users.ts")
        or name.endswith("/api/admin/users/[...path].ts")
    )
    manifest.setdefault("components", {})["auth_bff"] = {
        "ownership": "p2hs_managed",
        "paths": paths,
        "expected_fingerprints": {
            path: _sha(all_auth_files[path]) for path in paths
        },
        "extension_policy": "locked",
    }
    manifest["components"]["auth_password_surface"] = {
        "ownership": "p2hs_managed",
        "admitted": password,
        "paths": [
            path for path in paths
            if "password" in path or "forgot-password" in path or "reset-password" in path
        ],
        "extension_policy": "contract_gated",
    }
    return json.dumps(manifest, indent=2) + "\n"


def _workflow_target(source: str) -> str:
    if "name: Deploy to Cloudflare Pages" not in source or "cloudflare/wrangler-action" not in source:
        raise ValueError("deployment_authority_unproven")
    if "npm install" not in source:
        return source
    return source.replace("npm install", "test -f package-lock.json && npm ci")


_SESSION_AUTHORIZATION_COMPAT = r'''

// P2HS auth compatibility surface. Kept in the shared adapter because older
// generated admin BFFs import this helper from p2hs-records.mjs.
const P2HS_ACCESS_COOKIE = "__Host-p2hs_access";
function p2hsCookieValue(request, name) {
  const header = request?.headers?.get?.("cookie") || "";
  const part = header.split(";").map((item) => item.trim()).find((item) => item.startsWith(`${name}=`));
  return part ? decodeURIComponent(part.slice(name.length + 1)) : "";
}
export function sessionAuthorization(request) {
  const bearer = request?.headers?.get?.("authorization") || "";
  const cookie = p2hsCookieValue(request, P2HS_ACCESS_COOKIE);
  return bearer.startsWith("Bearer ") ? bearer : (cookie ? `Bearer ${cookie}` : "");
}
'''


def compile_bounded_auth_migration(
    repo_path: str | Path,
    project_type: str,
    runtime_contract: dict[str, Any],
    *,
    ownership: dict[str, str] | None = None,
) -> BoundedMigrationPlan:
    """Compile only auth/runtime/metadata/deployment operations."""
    if password_admitted(runtime_contract) is None:
        raise ValueError("bounded_executor_requires_explicit_auth_target")
    root = Path(repo_path)
    ownership = ownership or {}
    auth = auth_users_adapter_files(project_type, runtime_contract)
    sdk = app_sdk_files(project_type, runtime_contract)
    existing_managed: dict[str, str] = {}
    for path in root.rglob("*"):
        if not path.is_file() or any(part in {".git", "node_modules", "dist", "build"} for part in path.parts):
            continue
        relative = path.relative_to(root).as_posix()
        if password_admitted(runtime_contract) is False and relative in {
            "functions/api/auth/forgot-password.js",
            "functions/api/auth/reset-password.js",
            "server/api/auth/forgot-password.ts",
            "server/api/auth/forgot-password.post.ts",
            "server/api/auth/reset-password.ts",
            "server/api/auth/reset-password.post.ts",
        }:
            continue
        platform_surface = _is_platform_adapter(path)
        if not platform_surface:
            try:
                source_text = path.read_text(encoding="utf-8")
            except (OSError, UnicodeDecodeError):
                source_text = ""
            platform_surface = (
                "p2hs-auth-magic" in source_text
                or "Platform magic-link consume page" in source_text
                or "Generated Platform Port Catalog projection" in source_text
                or "Platform Port Catalog runtime. Installed by Core" in source_text
                or "p2hs-ports" in source_text
                or "p2hs-records" in source_text
            )
        if (
            relative.startswith("functions/api/auth/")
            or relative.startswith("functions/_lib/p2hs-auth-")
            or relative in {
                "functions/_lib/p2hs-ports.mjs",
                "functions/_lib/p2hs-port-contract.mjs",
                "functions/_lib/p2hs-records.mjs",
            }
        ) and platform_surface:
            existing_managed[relative] = path.read_bytes().decode("utf-8")
    target: dict[str, str] = {}
    target.update({
        path: content for path, content in auth.items()
        if (
            "p2hs-auth-" in path
            or "/api/admin/users" in path
            or "forgot-password" in path
            or "reset-password" in path
        )
    })
    runtime_paths = {
        path for path in sdk if path.endswith("/p2hs-app.js") or path.endswith("/p2hs-app.ts")
    }
    target.update({path: sdk[path] for path in runtime_paths})
    records_adapter = root / "functions/_lib/p2hs-records.mjs"
    if records_adapter.is_file():
        records_source = records_adapter.read_text(encoding="utf-8")
        if "export function sessionAuthorization" not in records_source:
            target[records_adapter.relative_to(root).as_posix()] = (
                records_source.rstrip() + _SESSION_AUTHORIZATION_COMPAT
            )
    target["p2hs/managed-runtime.json"] = _managed_runtime_manifest(
        sdk, auth, password=password_admitted(runtime_contract) is True,
        existing_managed=existing_managed,
    )
    target["p2hs/dependency-build.json"] = json.dumps(
        dependency_contract_manifest(), indent=2
    ) + "\n"
    gitignore = root / ".gitignore"
    if gitignore.is_file():
        ignore_text = gitignore.read_text(encoding="utf-8")
        if "!p2hs/managed-runtime.json" not in ignore_text:
            target[".gitignore"] = ignore_text.rstrip() + (
                "\n\n# P2HS tracked governance metadata\n"
                "!p2hs/\n"
                "p2hs/*\n"
                "!p2hs/managed-runtime.json\n"
                "!p2hs/dependency-build.json\n"
                "!p2hs/platform-ports.lock.json\n"
            )

    workflow = root / ".github/workflows/deploy.yml"
    if workflow.is_file():
        target[".github/workflows/deploy.yml"] = _workflow_target(
            workflow.read_text(encoding="utf-8")
        )

    removable = {
        "functions/api/auth/forgot-password.js",
        "functions/api/auth/reset-password.js",
        "server/api/auth/forgot-password.ts",
        "server/api/auth/forgot-password.post.ts",
        "server/api/auth/reset-password.ts",
        "server/api/auth/reset-password.post.ts",
    }
    existing_removable = sorted(path for path in removable if (root / path).is_file()) if password_admitted(runtime_contract) is False else []
    for path in existing_removable:
        if not _is_platform_adapter(root / path):
            raise ValueError(f"path_authority_blocked:{path}")

    managed_targets = set(target) | set(existing_removable)
    for path in managed_targets:
        if path in existing_removable or path not in target:
            continue
        current = root / path
        runtime_owned = path in runtime_paths and current.is_file()
        compatibility_owned = False
        if current.is_file() and path == "functions/_lib/p2hs-records.mjs":
            try:
                compatibility_owned = "p2hs-records" in current.read_text(encoding="utf-8")
            except (OSError, UnicodeDecodeError):
                compatibility_owned = False
        if current.is_file() and not (_is_platform_adapter(current) or runtime_owned or compatibility_owned) and path not in TRACKED_CANONICAL_METADATA and path not in {".gitignore", ".github/workflows/deploy.yml"}:
            raise ValueError(f"path_authority_blocked:{path}")
        expected = ownership.get(path)
        if expected and expected not in {"P2HS_MANAGED_UNMODIFIED", "P2HS_MANAGED_WITH_EXPECTED_FINGERPRINT", "P2HS_MANAGED_DEPLOYMENT"}:
            raise ValueError(f"path_authority_blocked:{path}")

    operations: list[BoundedMigrationOperation] = []
    auth_paths = tuple(sorted(
        set(auth)
        | set(existing_removable)
        | ({"functions/_lib/p2hs-records.mjs"} if "functions/_lib/p2hs-records.mjs" in target else set())
    ))
    if auth_paths:
        operations.append(BoundedMigrationOperation(
            "AUTH_PASSWORD_SURFACE_REMOVE" if password_admitted(runtime_contract) is False else "AUTH_PASSWORD_SURFACE_PRESERVE",
            "auth_password_surface", "auth-session-v4",
            auth_paths,
            {path: _fingerprint(root / path) for path in auth_paths},
            {path: None if path in existing_removable else _sha(target[path]) for path in auth_paths},
            "P2HS_MANAGED_UNMODIFIED",
            ("AUTH-GEN", "passwordless negative paths", "managed-runtime integrity"),
        ))
    runtime_paths_all = tuple(sorted(set(runtime_paths)))
    operations.append(BoundedMigrationOperation(
        "AUTH_RUNTIME_UPGRADE", "auth_session_runtime", "auth-session-v4",
        runtime_paths_all,
        {path: _fingerprint(root / path) for path in runtime_paths_all},
        {path: _sha(target[path]) for path in runtime_paths_all},
        "P2HS_MANAGED_UNMODIFIED",
        ("session-storage scan", "managed-runtime integrity"),
    ))
    metadata_paths = ("p2hs/managed-runtime.json",)
    if ".gitignore" in target:
        metadata_paths = (".gitignore", *metadata_paths)
    operations.append(BoundedMigrationOperation(
        "MANAGED_RUNTIME_MANIFEST_WRITE", "managed_runtime_metadata", "phase5e4e.v1",
        metadata_paths,
        {path: _fingerprint(root / path) for path in metadata_paths},
        {path: _sha(target[path]) for path in metadata_paths},
        "P2HS_CERTIFIED_MIGRATION_METADATA",
        ("manifest fingerprints", "provenance_kind=certified_migration"),
    ))
    operations.append(BoundedMigrationOperation(
        "DEPENDENCY_LOCK_ADOPT", "dependency_build", "dependency-build-v1",
        ("package-lock.json", "p2hs/dependency-build.json"),
        {"package-lock.json": _fingerprint(root / "package-lock.json"), "p2hs/dependency-build.json": _fingerprint(root / "p2hs/dependency-build.json")},
        {"package-lock.json": None, "p2hs/dependency-build.json": _sha(target["p2hs/dependency-build.json"])},
        "P2HS_CERTIFIED_MIGRATION_METADATA",
        ("lockfileVersion=3", "npm ci", "clean build"),
    ))
    if workflow.is_file() and target[".github/workflows/deploy.yml"] != workflow.read_text(encoding="utf-8"):
        operations.append(BoundedMigrationOperation(
            "DEPLOYMENT_INSTALL_CONTRACT_UPGRADE", "managed_deployment", "dependency-build-v1",
            (".github/workflows/deploy.yml",),
            {".github/workflows/deploy.yml": _fingerprint(workflow)},
            {".github/workflows/deploy.yml": _sha(target[".github/workflows/deploy.yml"])},
            "P2HS_MANAGED_DEPLOYMENT",
            ("npm ci", "package-lock present", "workflow ownership"),
        ))
    expected = tuple(sorted(set(target) | set(existing_removable) | {"package-lock.json"}))
    return BoundedMigrationPlan(project_type, tuple(operations), expected, target)


def apply_bounded_auth_migration(
    repo_path: str | Path, plan: BoundedMigrationPlan
) -> dict[str, list[str]]:
    root = Path(repo_path)
    changed: list[str] = []
    for operation in plan.operations:
        for path in operation.owned_paths:
            if path in {"package-lock.json"}:
                continue
            target = plan.target_files.get(path)
            file_path = root / path
            if target is None:
                if file_path.is_file():
                    file_path.unlink()
                    changed.append(path)
                continue
            file_path.parent.mkdir(parents=True, exist_ok=True)
            before = file_path.read_text(encoding="utf-8") if file_path.is_file() else None
            if before != target:
                file_path.write_text(target, encoding="utf-8", newline="\n")
                changed.append(path)
    return {"changed": sorted(set(changed))}
