#!/usr/bin/env python3
"""Enforce frozen waterline thresholds from freeze policy docs.

Policy source:
- docs/waterline-freeze-policy.md#non-regression-policy
- docs/waterline-freeze-policy.md#contract-coverage-baseline
"""

from __future__ import annotations

import argparse
import json
import re
import subprocess
import sys
from dataclasses import dataclass
from pathlib import Path


@dataclass(frozen=True)
class ContractBaseline:
    """Frozen minimum contract coverage for one module."""

    module_suffix: str
    minimum_percent: int


@dataclass(frozen=True)
class WaterlinePolicy:
    """Thresholds extracted from docs/waterline-freeze-policy.md."""

    error_budget: int
    warning_budget: int
    escape_hatch_budget: int
    contract_baselines: tuple[ContractBaseline, ...]


@dataclass(frozen=True)
class GuardMetrics:
    """Metrics consumed from `uvx invar-tools guard --all` output."""

    status: str
    static_errors: int
    static_warnings: int
    escape_hatches: int
    contract_by_file: dict[str, int]


class PolicyParseError(RuntimeError):
    """Raised when freeze policy document is missing required values."""


class GateFailure(RuntimeError):
    """Raised when a waterline criterion is violated."""


def _extract_int(pattern: str, text: str, label: str) -> int:
    match = re.search(pattern, text)
    if match is None:
        raise PolicyParseError(f"Missing policy value for {label}: pattern {pattern!r}")
    return int(match.group(1))


def parse_policy(policy_path: Path) -> WaterlinePolicy:
    """Load thresholds from freeze policy markdown.

    Source: docs/waterline-freeze-policy.md section "Hard Constraints" and
    section "Contract Coverage Baseline".
    """

    text = policy_path.read_text(encoding="utf-8")

    error_budget = _extract_int(
        r"Static errors MUST remain at (\d+)", text, "error budget"
    )
    warning_budget = _extract_int(
        r"Warnings MUST NOT exceed (\d+)", text, "warning budget"
    )
    escape_hatch_budget = _extract_int(
        r"escape hatches MUST NOT exceed (\d+)",
        text,
        "escape hatch budget",
    )

    contract_baselines: list[ContractBaseline] = []
    for row in re.finditer(
        r"\|\s*`([^`]+)`\s*\|\s*(\d+)%\s*\|",
        text,
    ):
        module_suffix = row.group(1).strip()
        minimum_percent = int(row.group(2))
        contract_baselines.append(
            ContractBaseline(
                module_suffix=module_suffix, minimum_percent=minimum_percent
            )
        )

    if not contract_baselines:
        raise PolicyParseError(
            "No contract baseline rows found in policy table. "
            "Expected rows like: | `core/foo.py` | 75% |"
        )

    return WaterlinePolicy(
        error_budget=error_budget,
        warning_budget=warning_budget,
        escape_hatch_budget=escape_hatch_budget,
        contract_baselines=tuple(contract_baselines),
    )


def run_guard(command: list[str]) -> GuardMetrics:
    """Run guard command and parse JSON output."""

    result = subprocess.run(command, capture_output=True, text=True, check=False)

    if result.returncode != 0:
        stderr_text = result.stderr.strip()
        stdout_text = result.stdout.strip()
        combined = stderr_text or stdout_text
        raise GateFailure(
            "FAIL-FAST[F0]: invar guard command failed before evaluation. "
            f"Exit={result.returncode}. Output: {combined}"
        )

    try:
        payload = json.loads(result.stdout)
    except json.JSONDecodeError as exc:
        raise GateFailure(
            "FAIL-FAST[F0]: guard output is not valid JSON; cannot enforce policy"
        ) from exc

    contract_by_file: dict[str, int] = {}
    for fix in payload.get("fixes", []):
        if fix.get("rule") != "contract_quality_ratio":
            continue

        file_path = str(fix.get("file", ""))
        message = str(fix.get("message", ""))
        percent_match = re.search(r"Contract coverage:\s*(\d+)%", message)
        if percent_match is None:
            continue
        contract_by_file[file_path] = int(percent_match.group(1))

    static = payload.get("static", {})
    escape_hatches = payload.get("escape_hatches", {})

    return GuardMetrics(
        status=str(payload.get("status", "")),
        static_errors=int(static.get("errors", 0)),
        static_warnings=int(static.get("warnings", 0)),
        escape_hatches=int(escape_hatches.get("count", 0)),
        contract_by_file=contract_by_file,
    )


def _lookup_contract_coverage(
    contract_by_file: dict[str, int], module_suffix: str
) -> int | None:
    for file_path, percent in contract_by_file.items():
        if file_path.endswith(module_suffix):
            return percent
    return None


def enforce_waterline(policy: WaterlinePolicy, metrics: GuardMetrics) -> list[str]:
    """Enforce fail-fast checks in priority order.

    Source ordering: docs/waterline-freeze-policy.md#verification-cycle-protocol
    lists status/errors/warnings/escape validation first; contract baseline
    non-regression is then enforced per frozen baseline table.
    """

    checks: list[str] = []

    if metrics.status != "passed":
        raise GateFailure(
            "FAIL-FAST[F1]: guard status regression. "
            f"Observed={metrics.status!r}, required='passed'."
        )
    checks.append("F1 guard status=passed")

    if metrics.static_errors > policy.error_budget:
        raise GateFailure(
            "FAIL-FAST[F2]: static error budget exceeded. "
            f"Observed={metrics.static_errors}, budget={policy.error_budget}."
        )
    checks.append(f"F2 static errors={metrics.static_errors}/{policy.error_budget}")

    if metrics.static_warnings > policy.warning_budget:
        raise GateFailure(
            "FAIL-FAST[F3]: warning budget exceeded. "
            f"Observed={metrics.static_warnings}, budget={policy.warning_budget}."
        )
    checks.append(
        f"F3 static warnings={metrics.static_warnings}/{policy.warning_budget}"
    )

    if metrics.escape_hatches > policy.escape_hatch_budget:
        raise GateFailure(
            "FAIL-FAST[F4]: escape hatch budget exceeded. "
            f"Observed={metrics.escape_hatches}, budget={policy.escape_hatch_budget}."
        )
    checks.append(
        f"F4 escape hatches={metrics.escape_hatches}/{policy.escape_hatch_budget}"
    )

    for baseline in policy.contract_baselines:
        observed = _lookup_contract_coverage(
            metrics.contract_by_file, baseline.module_suffix
        )
        if observed is None:
            checks.append(
                f"F5 contract {baseline.module_suffix}: no active warning (>=80% or not regressed)"
            )
            continue

        if observed < baseline.minimum_percent:
            raise GateFailure(
                "FAIL-FAST[F5]: contract coverage regressed below frozen baseline. "
                f"Module={baseline.module_suffix}, observed={observed}%, "
                f"baseline={baseline.minimum_percent}%."
            )
        checks.append(
            f"F5 contract {baseline.module_suffix}: {observed}%/{baseline.minimum_percent}%"
        )

    return checks


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        description="Enforce frozen waterline thresholds from policy docs."
    )
    parser.add_argument(
        "--policy",
        type=Path,
        default=Path("docs/waterline-freeze-policy.md"),
        help="Path to freeze policy markdown (default: docs/waterline-freeze-policy.md)",
    )
    parser.add_argument(
        "--guard-command",
        nargs="+",
        default=["uvx", "invar-tools", "guard", "--all"],
        help="Guard command that outputs JSON (default: uvx invar-tools guard --all)",
    )
    return parser


def main() -> int:
    parser = build_parser()
    args = parser.parse_args()

    try:
        policy = parse_policy(args.policy)
        metrics = run_guard(args.guard_command)
        checks = enforce_waterline(policy, metrics)
    except (PolicyParseError, GateFailure) as exc:
        print(str(exc), file=sys.stderr)
        return 1

    print("WATERLINE GATE: PASS")
    for check in checks:
        print(f" - {check}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
