"""
Phase 91: API Versioning for Backward Compatibility.

Register versioned API route sets, deprecate old versions with sunset dates,
resolve versioned routes, and generate migration guides between versions.

Storage:
  state/broker/api_versions/versions.json   -- version metadata + routes
  state/broker/api_versions/changes.jsonl   -- breaking/non-breaking changes
"""

import json
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Callable, Optional

from brynq.server import BROKER_DIR, _write_json_sync, _read_jsonl_sync, _append_jsonl_sync

# ── Paths ──────────────────────────────────────────────────────────────────

API_VER_DIR = BROKER_DIR / "api_versions"
API_VER_DIR.mkdir(parents=True, exist_ok=True)

VERSIONS_FILE = API_VER_DIR / "versions.json"
CHANGES_FILE = API_VER_DIR / "changes.jsonl"

# ── Persistence Helpers ────────────────────────────────────────────────────


def _load_versions() -> dict:
    try:
        if VERSIONS_FILE.exists():
            return json.loads(VERSIONS_FILE.read_text("utf-8"))
    except (json.JSONDecodeError, OSError):
        pass
    return {}


def _save_versions(data: dict) -> None:
    _write_json_sync(VERSIONS_FILE, data)


# ── Public API ─────────────────────────────────────────────────────────────


def register_version(version: str, routes: dict, deprecated: bool = False) -> dict:
    """Register an API version with its *routes* map {path: handler_name}.

    Returns the version record.
    """
    versions = _load_versions()
    versions[version] = {
        "version": version,
        "routes": routes,
        "deprecated": deprecated,
        "sunset_date": None,
        "created_at": datetime.now(timezone.utc).isoformat(),
    }
    _save_versions(versions)
    return {"ok": True, "version": version}


def get_version(version: str) -> Optional[dict]:
    """Return metadata for a single API version, or None."""
    return _load_versions().get(version)


def list_versions(status: Optional[str] = None) -> list[dict]:
    """List all registered versions, optionally filtered by *status*.

    status: 'active', 'deprecated', or None for all.
    """
    versions = _load_versions()
    result = list(versions.values())
    if status == "active":
        result = [v for v in result if not v.get("deprecated")]
    elif status == "deprecated":
        result = [v for v in result if v.get("deprecated")]
    result.sort(key=lambda v: v.get("version", ""))
    return result


def resolve_route(version: str, path: str) -> Optional[dict]:
    """Find the handler for a versioned route.

    Returns {version, path, handler} or None if not found.
    Falls back to the highest version that has the route.
    """
    versions = _load_versions()
    ver = versions.get(version)
    if ver:
        handler = ver.get("routes", {}).get(path)
        if handler:
            return {"version": version, "path": path, "handler": handler,
                    "deprecated": ver.get("deprecated", False)}

    # Fallback: search all versions descending
    for v_key in sorted(versions.keys(), reverse=True):
        v = versions[v_key]
        handler = v.get("routes", {}).get(path)
        if handler:
            return {"version": v_key, "path": path, "handler": handler,
                    "deprecated": v.get("deprecated", False), "fallback": True}
    return None


def deprecate_version(version: str, sunset_date: Optional[str] = None) -> dict:
    """Mark a version as deprecated with an optional sunset date."""
    versions = _load_versions()
    if version not in versions:
        return {"error": f"Version '{version}' not found"}
    versions[version]["deprecated"] = True
    versions[version]["sunset_date"] = sunset_date
    _save_versions(versions)
    return {"ok": True, "version": version, "sunset_date": sunset_date}


def add_change(from_version: str, to_version: str, change_type: str,
               description: str) -> dict:
    """Record a breaking or non-breaking change between versions.

    change_type: 'breaking', 'non-breaking', 'addition', 'removal'.
    """
    valid_types = {"breaking", "non-breaking", "addition", "removal"}
    if change_type not in valid_types:
        return {"error": f"change_type must be one of {sorted(valid_types)}"}
    entry = {
        "from_version": from_version,
        "to_version": to_version,
        "change_type": change_type,
        "description": description,
        "recorded_at": datetime.now(timezone.utc).isoformat(),
    }
    _append_jsonl_sync(CHANGES_FILE, entry)
    return {"ok": True, **entry}


def get_migration_guide(from_version: str, to_version: str) -> dict:
    """List all changes between two versions."""
    changes = _read_jsonl_sync(CHANGES_FILE)
    relevant = [
        c for c in changes
        if c.get("from_version") == from_version and c.get("to_version") == to_version
    ]
    breaking = [c for c in relevant if c.get("change_type") == "breaking"]
    non_breaking = [c for c in relevant if c.get("change_type") != "breaking"]
    return {
        "from_version": from_version,
        "to_version": to_version,
        "total_changes": len(relevant),
        "breaking_changes": breaking,
        "other_changes": non_breaking,
    }
