#!/usr/bin/env python3
"""Validate the focused frontier's source-verification projection."""

from __future__ import annotations

import argparse
import json
import sys
import tomllib
from datetime import datetime, timezone
from pathlib import Path


def parse_instant(value: str) -> datetime:
    normalized = value.replace("Z", "+00:00")
    parsed = datetime.fromisoformat(normalized)
    if parsed.tzinfo is None:
        parsed = parsed.replace(tzinfo=timezone.utc)
    return parsed.astimezone(timezone.utc)


def validate_sources(frontier: Path, max_age_days: int) -> dict:
    issues: list[dict[str, str]] = []
    config_path = frontier / ".vela" / "config.toml"
    source_path = frontier / "decision" / "source-verification.v1.json"

    if not config_path.is_file():
        issues.append({"id": "frontier", "field": "config", "message": "missing .vela/config.toml"})
        frontier_id = ""
    else:
        with config_path.open("rb") as handle:
            config = tomllib.load(handle)
        frontier_id = config.get("project", {}).get("frontier_id", "")

    if not source_path.is_file():
        return {
            "ok": False,
            "command": "check-source-freshness",
            "frontier": str(frontier),
            "frontier_id": frontier_id,
            "source_count": 0,
            "issue_count": 1,
            "verified_at": None,
            "max_age_days": max_age_days,
            "issues": [{"id": "source-verification", "field": "file", "message": "missing decision/source-verification.v1.json"}],
        }

    projection = json.loads(source_path.read_text())
    if projection.get("schema") != "vela.source-verification.v1":
        issues.append({"id": "source-verification", "field": "schema", "message": "expected vela.source-verification.v1"})

    if frontier_id and projection.get("frontier_id") != frontier_id:
        issues.append({"id": "source-verification", "field": "frontier_id", "message": "frontier id does not match .vela/config.toml"})

    verified_at = projection.get("verified_at")
    verified_age_days = None
    if not isinstance(verified_at, str) or not verified_at:
        issues.append({"id": "source-verification", "field": "verified_at", "message": "verified_at is required"})
    else:
        try:
            verified = parse_instant(verified_at)
            now = datetime.now(timezone.utc)
            verified_age_days = (now - verified).total_seconds() / 86400
            if verified_age_days < -1:
                issues.append({"id": "source-verification", "field": "verified_at", "message": "verified_at is in the future"})
            if verified_age_days > max_age_days:
                issues.append({"id": "source-verification", "field": "verified_at", "message": f"source verification is older than {max_age_days} days"})
        except ValueError as exc:
            issues.append({"id": "source-verification", "field": "verified_at", "message": f"invalid timestamp: {exc}"})

    sources = projection.get("sources", [])
    if not isinstance(sources, list) or not sources:
        issues.append({"id": "source-verification", "field": "sources", "message": "sources must be a non-empty list"})
        sources = []

    required_fields = ("id", "title", "url", "agency", "current_status")
    seen_ids: set[str] = set()
    for source in sources:
        source_id = source.get("id") if isinstance(source, dict) else None
        issue_id = source_id if isinstance(source_id, str) and source_id else "source"
        if source_id in seen_ids:
            issues.append({"id": issue_id, "field": "id", "message": "duplicate source id"})
        if isinstance(source_id, str):
            seen_ids.add(source_id)

        if not isinstance(source, dict):
            issues.append({"id": issue_id, "field": "source", "message": "source row must be an object"})
            continue

        for field in required_fields:
            if not isinstance(source.get(field), str) or not source.get(field, "").strip():
                issues.append({"id": issue_id, "field": field, "message": f"{field} is required"})

        url = source.get("url")
        if isinstance(url, str) and not url.startswith(("http://", "https://")):
            issues.append({"id": issue_id, "field": "url", "message": "url must be http(s)"})

    return {
        "ok": len(issues) == 0,
        "command": "check-source-freshness",
        "frontier": str(frontier),
        "frontier_id": frontier_id,
        "source_count": len(sources),
        "issue_count": len(issues),
        "verified_at": verified_at,
        "verified_age_days": verified_age_days,
        "max_age_days": max_age_days,
        "issues": issues,
    }


def main() -> int:
    parser = argparse.ArgumentParser(description="Validate current-source freshness for a Vela frontier.")
    parser.add_argument("frontier", type=Path)
    parser.add_argument("--max-age-days", type=int, default=45)
    parser.add_argument("--json", action="store_true", dest="json_output")
    args = parser.parse_args()

    result = validate_sources(args.frontier.resolve(), args.max_age_days)
    if args.json_output:
        print(json.dumps(result, indent=2, sort_keys=True))
    else:
        status = "ok" if result["ok"] else "failed"
        print(f"source freshness: {status}")
        print(f"sources: {result['source_count']}")
        print(f"verified_at: {result['verified_at']}")
        for issue in result["issues"]:
            print(f"{issue['id']} {issue['field']}: {issue['message']}")
    return 0 if result["ok"] else 1


if __name__ == "__main__":
    sys.exit(main())
