#!/usr/bin/env python3
"""Codex Stop hook: validate discovered C/C++ projects with MSVC."""

import json
import os
import subprocess
import sys
import time
from pathlib import Path


SCRIPTS = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(SCRIPTS))

from msvc_harness.adapters.base import AdapterError
from msvc_harness.adapters.cmake import CMakeAdapter
from msvc_harness.adapters.msbuild import MSBuildAdapter
from msvc_harness.config import ConfigError, load_config
from msvc_harness.discovery import DiscoveryError, discover_project
from msvc_harness.models import ProjectKind
from msvc_harness.process import ValidationFailure, execute_plan
from msvc_harness.toolchain import ToolchainError, discover_toolchain


REENTRY_ENV = "CODEX_STOP_VALIDATION_ACTIVE"
TOTAL_TIMEOUT_SECONDS = 1800


def _remaining(deadline, clock, stage):
    remaining = deadline - clock()
    if remaining <= 0:
        raise ValidationFailure(f"{stage} timed out before it could start")
    return remaining


def _project_root(
    cwd: Path,
    *,
    deadline: float,
    run=subprocess.run,
    clock=time.monotonic,
) -> Path:
    argv = ["git", "rev-parse", "--show-toplevel"]
    resolved_cwd = cwd.resolve()
    try:
        result = run(
            argv,
            cwd=resolved_cwd,
            capture_output=True,
            text=True,
            shell=False,
            timeout=_remaining(deadline, clock, "repository discovery"),
        )
    except subprocess.TimeoutExpired as exc:
        raise ValidationFailure(
            f"repository discovery timed out; argv={argv!r}; "
            f"cwd={str(resolved_cwd)!r}"
        ) from exc
    if result.returncode == 0 and result.stdout.strip():
        return Path(result.stdout.strip()).resolve()
    return resolved_cwd


def run_validations(
    root: Path,
    *,
    deadline: float | None = None,
    clock=time.monotonic,
) -> tuple[bool, str]:
    """Build and test a discovered C/C++ project, when one exists."""
    if deadline is None:
        deadline = clock() + TOTAL_TIMEOUT_SECONDS
    try:
        config = load_config(root)
        discovery = discover_project(root, config)
        if discovery.selection is None:
            return True, ""
        selection = discovery.selection
        tools = discover_toolchain(
            selection.kind,
            deadline=deadline,
            clock=clock,
        )
        adapter = (
            CMakeAdapter()
            if selection.kind is ProjectKind.CMAKE
            else MSBuildAdapter()
        )
        plan = adapter.create_plan(root, selection, config, tools)
        child_env = os.environ.copy()
        child_env[REENTRY_ENV] = "1"
        execute_plan(
            plan,
            root,
            env=child_env,
            deadline=deadline,
            clock=clock,
        )
        return True, ""
    except (
        ConfigError,
        DiscoveryError,
        ToolchainError,
        AdapterError,
        ValidationFailure,
        OSError,
    ) as exc:
        return False, str(exc)


def _emit_stop_response(message: str) -> None:
    print(
        json.dumps(
            {
                "continue": False,
                "stopReason": message,
                "systemMessage": message,
            },
            ensure_ascii=False,
        )
    )


def main(*, run=subprocess.run, clock=time.monotonic) -> int:
    if os.environ.get(REENTRY_ENV) == "1":
        return 0

    deadline = clock() + TOTAL_TIMEOUT_SECONDS
    try:
        root = _project_root(
            Path.cwd(),
            deadline=deadline,
            run=run,
            clock=clock,
        )
        ok, message = run_validations(root, deadline=deadline, clock=clock)
    except (OSError, ValidationFailure) as exc:
        _emit_stop_response(f"validation hook failed: {exc}")
        return 0

    if ok:
        return 0
    _emit_stop_response(message)
    return 0


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