"""Validate commit attestation."""

import json
import os
import sys
from pathlib import Path

import click
import yaml

from ..display.output import console, success, error, info, warning
from ..hooks.agent_detection import AGENT_ENV_VARS
from ._cache import _is_cache_stale
from ._shared import get_repo_root as _get_repo_root, get_staged_diff_hash as _get_staged_diff_hash, load_local_config, local_config_exists, strip_yaml_delimiters


def _parse_attestation(path: Path) -> dict[str, str]:
    """Parse attestation file using YAML safe_load."""
    content = strip_yaml_delimiters(path.read_text(encoding="utf-8"))
    try:
        # Use BaseLoader to keep all values as raw strings (no auto-conversion
        # of timestamps, booleans, etc.) — we only need string comparisons.
        data = yaml.load(content, Loader=yaml.BaseLoader)  # noqa: S506
        if not isinstance(data, dict):
            return {}
        result: dict[str, str] = {}
        for k, v in data.items():
            if not v:
                continue
            # Normalize keys: replace spaces with underscores (backward compat)
            key = str(k).replace(" ", "_")
            result[key] = str(v)
        return result
    except yaml.YAMLError:
        return {}


@click.command()
def check() -> None:
    """Validate commit attestation.

    Checks that .gjalla/.commit-attestation.yaml exists and its staged_diff_hash
    matches the current staged changes. Used by the pre-commit hook,
    also usable standalone.

    \b
    Exit codes:
        0 - Attestation valid (or no .gjalla config)
        1 - Attestation missing or stale
    """
    repo_root = _get_repo_root()

    # No .gjalla config = not a guardrails project, pass through
    if not local_config_exists(repo_root):
        # Also check old format (.gjalla as a file)
        old_config = repo_root / ".gjalla"
        if not old_config.is_file():
            return

    # Skip if env var set — but log it for audit trail
    if os.environ.get("SKIP_ATTESTATION") == "1":
        info("Attestation skipped (SKIP_ATTESTATION=1)")
        try:
            import subprocess
            import datetime
            branch = subprocess.run(
                ["git", "rev-parse", "--abbrev-ref", "HEAD"],
                capture_output=True, text=True, cwd=str(repo_root),
            ).stdout.strip() or "unknown"
            log_path = repo_root / ".gjalla" / "log.jsonl"
            (repo_root / ".gjalla" / "attestations").mkdir(parents=True, exist_ok=True)
            entry = json.dumps({
                "event": "attestation_skipped",
                "branch": branch,
                "timestamp": datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
                "synced": False,
            })
            with open(log_path, "a") as f:
                f.write(entry + "\n")
        except Exception:
            pass  # audit logging is best-effort
        return

    attestation_path = repo_root / ".gjalla" / ".commit-attestation.yaml"
    if not attestation_path.exists():
        diff_hash = _get_staged_diff_hash()
        is_agent = any(os.environ.get(v) for v in AGENT_ENV_VARS)

        console.print()
        console.print("[bold red]gjalla: Attestation Required[/bold red]")
        console.print()

        if is_agent:
            console.print("  YOU must create .gjalla/.commit-attestation.yaml before committing.")
            console.print()
            console.print("  1. Use the example at .gjalla/example-attestation.yaml for the format")
            console.print(f"  2. Set staged_diff_hash to: {diff_hash}", markup=False, highlight=False)
            console.print("  3. Fetch rules (get_project_rules) and architecture (get_architecture_spec)")
            console.print("  4. Fill in ALL primitives under the changes: key: architecture (elements,")
            console.print("     connections, decisions), data_model, data_flows, rules, capabilities,")
            console.print("     api_surface, external_dependencies, tech_stack")
            console.print("  5. Write the file, then retry git commit")
            console.print()
        else:
            console.print(f"  staged_diff_hash: {diff_hash}", markup=False, highlight=False)
            console.print()
            console.print("  Create .gjalla/.commit-attestation.yaml using the format in")
            console.print("  .gjalla/example-attestation.yaml.")
            console.print()
            console.print("  Human? Use `gjalla attest` interactively.")
            if sys.stdin.isatty():
                console.print("  Or skip: SKIP_ATTESTATION=1 git commit ...")
            console.print()
        raise SystemExit(1)

    # Validate diff hash
    fields = _parse_attestation(attestation_path)
    attested_hash = fields.get("staged_diff_hash", "")
    current_hash = _get_staged_diff_hash()

    if attested_hash != current_hash:
        error("Attestation stale — staged changes differ from attested hash")
        console.print(f"  Expected: {attested_hash[:16]}...")
        console.print(f"  Current:  {current_hash[:16]}...")
        raise SystemExit(1)

    success("Attestation verified")

    # Check cache freshness (non-blocking warning)
    try:
        config = load_local_config(repo_root)
        stale = _is_cache_stale(config)
        if stale is True:
            cache_meta = config.get("cache", {})
            last_synced = cache_meta.get("last_synced", "")
            if last_synced:
                from datetime import datetime, timezone
                synced_at = datetime.fromisoformat(last_synced.replace("Z", "+00:00"))
                age_days = (datetime.now(timezone.utc) - synced_at).days
                warning(f"Project context cache is {age_days} day(s) old. Run 'gjalla sync' to refresh.")
            else:
                warning("Project context cache is stale. Run 'gjalla sync' to refresh.")
    except Exception:
        pass  # cache staleness check is best-effort
