#!/usr/bin/env python3
"""Extract expected/loaded/applied skill usage signals from safe hook events."""

from __future__ import annotations

import argparse
import json
import pathlib
import sys
from typing import Any

from collect_hook_event import normalize_event, skill_from_path, snake
from evaluate_skill_routing import expected_for_scope, load_json


FAILURE_EVENTS = {"post_tool_use_failure", "validation_failure", "user_correction", "scope_failure"}
LOADED_EVENTS = {"instructions_loaded", "user_prompt_expansion", "file_read"}
APPLIED_EVENTS = {"skill_applied", "gate_applied", "validation_passed"}


def read_events(path: pathlib.Path) -> list[dict[str, Any]]:
    rows = []
    for line in path.read_text(encoding="utf-8").splitlines():
        if not line.strip():
            continue
        raw = json.loads(line)
        rows.append(normalize_event(raw, pathlib.Path(raw.get("cwd") or raw.get("repo") or ".")))
    return rows


def available_skills(skill_map: dict[str, Any]) -> list[str]:
    return sorted({item.get("name", "") for item in skill_map.get("skills", []) if item.get("valid", True)})


def session_id(event: dict[str, Any]) -> str:
    return str(event.get("session_id") or "session")


def build_usage(events: list[dict[str, Any]], skill_map: dict[str, Any], default_scope: str = "") -> dict[str, Any]:
    sessions: dict[str, dict[str, Any]] = {}
    for event in events:
        sid = session_id(event)
        row = sessions.setdefault(
            sid,
            {"session_id": sid, "scope": default_scope, "expected": [], "loaded": [], "applied": [], "missed": [], "failed": [], "correction": False},
        )
        if event.get("scope"):
            row["scope"] = event["scope"]
        event_name = snake(str(event.get("event", "")))
        skill = event.get("skill") or skill_from_path(event.get("path"))
        if event_name == "scope" and event.get("label"):
            row["scope"] = event["label"]
        if event_name in LOADED_EVENTS and skill and skill not in row["loaded"]:
            row["loaded"].append(skill)
        if event_name in APPLIED_EVENTS and skill and skill not in row["applied"]:
            row["applied"].append(skill)
        if event_name in FAILURE_EVENTS or event.get("outcome") in {"failure", "correction", "failed"}:
            row["correction"] = True

    if not sessions:
        sessions["session"] = {
            "session_id": "session",
            "scope": default_scope,
            "expected": [],
            "loaded": [],
            "applied": [],
            "missed": [],
            "failed": [],
            "correction": False,
        }

    aggregate = {"expected": set(), "loaded": set(), "applied": set(), "missed": set(), "failed": set()}
    for row in sessions.values():
        expected, _reason, _confidence = expected_for_scope(row.get("scope", ""), skill_map)
        row["expected"] = expected
        row["missed"] = [skill for skill in expected if skill not in row["loaded"]]
        loaded_not_applied = [skill for skill in expected if skill in row["loaded"] and skill not in row["applied"]]
        if row["correction"]:
            row["failed"] = sorted(set(row["loaded"] + row["applied"] + row["missed"] + loaded_not_applied))
        for key in aggregate:
            aggregate[key].update(row[key])

    return {
        "available": available_skills(skill_map),
        "expected": sorted(aggregate["expected"]),
        "loaded": sorted(aggregate["loaded"]),
        "applied": sorted(aggregate["applied"]),
        "missed": sorted(aggregate["missed"]),
        "failed": sorted(aggregate["failed"]),
        "sessions": sorted(sessions.values(), key=lambda row: row["session_id"]),
    }


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--events", required=True)
    parser.add_argument("--skill-map", required=True)
    parser.add_argument("--scope", default="")
    parser.add_argument("--output")
    args = parser.parse_args(argv)

    usage = build_usage(read_events(pathlib.Path(args.events)), load_json(args.skill_map), args.scope)
    rendered = json.dumps(usage, indent=2, sort_keys=True) + "\n"
    if args.output:
        pathlib.Path(args.output).write_text(rendered, encoding="utf-8")
    else:
        sys.stdout.write(rendered)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
