from __future__ import annotations

import json
import os
import threading
from datetime import datetime, timezone
from typing import Any

from persistence import secure_atomic_write

_USAGE_LOCK = threading.Lock()
_SCHEMA_VERSION = 1


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


def usage_path(skills_base: str) -> str:
    return os.path.join(skills_base, ".usage.json")


def load_usage(skills_base: str) -> dict[str, Any]:
    path = usage_path(skills_base)
    try:
        with open(path, "r", encoding="utf-8") as f:
            raw = json.load(f)
    except FileNotFoundError:
        return {"schema_version": _SCHEMA_VERSION, "skills": {}}
    except Exception:
        return {"schema_version": _SCHEMA_VERSION, "skills": {}}

    if not isinstance(raw, dict):
        return {"schema_version": _SCHEMA_VERSION, "skills": {}}

    # Accept old flat shape for forwards compatibility.
    if "skills" not in raw and all(isinstance(v, dict) for v in raw.values()):
        return {"schema_version": _SCHEMA_VERSION, "skills": raw}

    skills = raw.get("skills")
    if not isinstance(skills, dict):
        skills = {}
    return {"schema_version": int(raw.get("schema_version", _SCHEMA_VERSION)), "skills": skills}


def _save_usage(skills_base: str, data: dict[str, Any]) -> None:
    path = usage_path(skills_base)
    payload = json.dumps(data, indent=2, ensure_ascii=True, sort_keys=True)
    secure_atomic_write(path, payload)


def _ensure_skill_record(data: dict[str, Any], skill_name: str, now_iso: str) -> dict[str, Any]:
    skills = data.setdefault("skills", {})
    rec = skills.get(skill_name)
    if not isinstance(rec, dict):
        rec = {}
        skills[skill_name] = rec
    rec.setdefault("use_count", 0)
    rec.setdefault("view_count", 0)
    rec.setdefault("patch_count", 0)
    rec.setdefault("created_at", now_iso)
    rec.setdefault("pinned", False)
    rec.setdefault("state", "active")
    return rec


def mutate_usage(skills_base: str, mutator) -> dict[str, Any]:
    with _USAGE_LOCK:
        data = load_usage(skills_base)
        if callable(mutator):
            mutator(data)
        data["schema_version"] = _SCHEMA_VERSION
        _save_usage(skills_base, data)
        return data


def record_skill_event(
    *,
    skills_base: str,
    skill_name: str,
    event: str,
    state: str | None = None,
    pinned: bool | None = None,
    archived_at: str | None = None,
    absorbed_into: str | None = None,
) -> None:
    now_iso = _now_iso()
    with _USAGE_LOCK:
        data = load_usage(skills_base)
        rec = _ensure_skill_record(data, skill_name, now_iso)

        if event == "view":
            rec["view_count"] = int(rec.get("view_count", 0)) + 1
            rec["last_view_at"] = now_iso
        elif event == "patch":
            rec["patch_count"] = int(rec.get("patch_count", 0)) + 1
            rec["last_patch_at"] = now_iso
        elif event == "use":
            rec["use_count"] = int(rec.get("use_count", 0)) + 1
            rec["last_use_at"] = now_iso

        rec["last_activity_at"] = now_iso

        if state:
            rec["state"] = state
        if pinned is not None:
            rec["pinned"] = bool(pinned)
        if archived_at is not None:
            rec["archived_at"] = archived_at
        if absorbed_into is not None:
            rec["absorbed_into"] = absorbed_into

        data["schema_version"] = _SCHEMA_VERSION
        _save_usage(skills_base, data)
