from __future__ import annotations

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

from .config import CACHE_DIR, ensure_dirs, load_json, save_json


def utc_now() -> str:
    return datetime.now(timezone.utc).isoformat()


def cache_path(name: str) -> Path:
    safe = name.replace("/", "_").replace("..", "_")
    return CACHE_DIR / f"{safe}.json"


def read_cache(name: str) -> dict[str, Any] | None:
    return load_json(cache_path(name), default=None)


def write_cache(name: str, payload: Any, source: str | None = None) -> dict[str, Any]:
    ensure_dirs()
    wrapped = {
        "cached_at": utc_now(),
        "source": source,
        "payload": payload,
    }
    save_json(cache_path(name), wrapped)
    return wrapped


def age_seconds(item: dict[str, Any]) -> float | None:
    value = item.get("cached_at")
    if not value:
        return None
    try:
        return (datetime.now(timezone.utc) - datetime.fromisoformat(value)).total_seconds()
    except Exception:
        return None


def get_or_fetch(
    name: str,
    ttl_seconds: int,
    fetcher: Callable[[], Any],
    *,
    force: bool = False,
    source: str | None = None,
) -> dict[str, Any]:
    cached = read_cache(name)
    age = age_seconds(cached) if cached else None
    if cached and not force and age is not None and age <= ttl_seconds:
        cached["cache_status"] = "hit"
        return cached
    payload = fetcher()
    fresh = write_cache(name, payload, source=source)
    fresh["cache_status"] = "refresh"
    return fresh


def status(names: list[str]) -> list[dict[str, Any]]:
    out = []
    for name in names:
        item = read_cache(name)
        out.append(
            {
                "name": name,
                "exists": item is not None,
                "cached_at": item.get("cached_at") if item else None,
                "age_seconds": age_seconds(item) if item else None,
                "path": str(cache_path(name)),
            }
        )
    return out
