#!/usr/bin/env python3
"""
Expertise File Manager — per-cron compounding memory.

Each recurring cron job gets its own expertise file at
  ~/.hermes/expertise/<job-name>.yaml

The file is loaded at job start and updated at job end, making every run
smarter than the last.

Usage:
  # Load expertise into context (inline at top of cron prompt):
  python -m pipeline.expertise --load --job triage

  # Append a new insight after work is done:
  python -m pipeline.expertise --append --job triage --domain github-issues \
    --insight "Issues with label 'bug' most often miss reproduction steps"

  # Read all expertise entries (JSON for tool consumption):
  python -m pipeline.expertise --read --job triage
"""

from __future__ import annotations

import argparse
import json
import os
import sys
from datetime import datetime, timezone
from typing import Any, Optional

try:
    import yaml
except ModuleNotFoundError:
    class yaml:
        @staticmethod
        def safe_load(f):
            text = f.read()
            if not text.strip():
                return None
            try:
                return json.loads(text)
            except json.JSONDecodeError as exc:
                # ponytail: no silent YAML-to-empty fallback; install PyYAML for real YAML files.
                raise RuntimeError("PyYAML is required to read non-JSON expertise files") from exc

        @staticmethod
        def dump(data, f, **_kwargs):
            json.dump(data, f, indent=2, ensure_ascii=False)


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


def expertise_dir() -> str:
    """Return ~/.hermes/expertise/ directory, creating it if needed."""
    base = os.path.join(os.path.expanduser("~"), ".hermes", "expertise")
    os.makedirs(base, exist_ok=True)
    return base


def expertise_path(job_name: str) -> str:
    return os.path.join(expertise_dir(), f"{job_name}.yaml")


# ── Data Model ────────────────────────────────────────────────────────────────


ENTRY_SCHEMA = {
    "domain": str,
    "insight": str,
    "date": str,
    "source": str,
    "verified": bool,
}


def default_expertise() -> dict:
    return {
        "updatable": True,
        "version": 1,
        "entries": [],
    }


# ── Load / Save ───────────────────────────────────────────────────────────────


def load(job_name: str) -> dict:
    path = expertise_path(job_name)
    if os.path.exists(path):
        with open(path, "r", encoding="utf-8") as f:
            data = yaml.safe_load(f) or default_expertise()
            data.setdefault("updatable", True)
            data.setdefault("version", 1)
            data.setdefault("entries", [])
            return data
    return default_expertise()


def save(job_name: str, data: dict) -> None:
    path = expertise_path(job_name)
    with open(path, "w", encoding="utf-8") as f:
        yaml.dump(
            data,
            f,
            default_flow_style=False,
            sort_keys=False,
            allow_unicode=True,
        )


# ── Operations ────────────────────────────────────────────────────────────────


def append_entry(
    job_name: str,
    domain: str,
    insight: str,
    source: Optional[str] = None,
    verified: bool = False,
) -> dict:
    """Append a new insight entry to the job's expertise file."""
    data = load(job_name)
    if not data.get("updatable", True):
        print(f"Expertise file for '{job_name}' is not updatable.", file=sys.stderr)
        return data

    entry = {
        "domain": domain,
        "insight": insight,
        "date": datetime.now(timezone.utc).strftime("%Y-%m-%d"),
        "source": source or f"cron:{job_name}",
        "verified": verified,
    }
    data["entries"].append(entry)
    save(job_name, data)
    return data


def render_context(job_name: str) -> str:
    """Render expertise entries as a context string for agent prompts."""
    data = load(job_name)
    if not data["entries"]:
        return ""
    lines = [f"# Expertise ({job_name})"]
    for i, e in enumerate(data["entries"], 1):
        tag = "✓" if e.get("verified") else "·"
        lines.append(f"{tag} [{e['domain']}] {e['insight']} ({e['date']})")
    return "\n".join(lines)


def purge_domain(job_name: str, domain: str) -> dict:
    """Remove all entries for a specific domain."""
    data = load(job_name)
    before = len(data["entries"])
    data["entries"] = [e for e in data["entries"] if e.get("domain") != domain]
    removed = before - len(data["entries"])
    if removed:
        save(job_name, data)
        print(f"Removed {removed} entries for domain '{domain}'.")
    else:
        print(f"No entries found for domain '{domain}'.")
    return data


# ── CLI ───────────────────────────────────────────────────────────────────────


def main():
    parser = argparse.ArgumentParser(description="Expertise file manager")
    parser.add_argument("--job", required=True, help="Job name (corresponds to expertise filename)")
    parser.add_argument("--load", action="store_true", help="Print expertise as context (for injection at cron start)")
    parser.add_argument("--read", action="store_true", help="Dump entire expertise file as JSON")
    parser.add_argument("--append", action="store_true", help="Append a new insight entry")
    parser.add_argument("--domain", default="general", help="Domain tag for the insight")
    parser.add_argument("--insight", help="The insight text to store")
    parser.add_argument("--source", help="Source identifier (default: cron:<job>)")
    parser.add_argument("--verified", action="store_true", default=False, help="Mark insight as verified")
    parser.add_argument("--purge", help="Remove all entries for a domain")

    args = parser.parse_args()

    if args.load:
        print(render_context(args.job))
        return

    if args.read:
        data = load(args.job)
        print(json.dumps(data, indent=2))
        return

    if args.purge:
        purge_domain(args.job, args.purge)
        return

    if args.append:
        if not args.insight:
            print("ERROR: --insight is required with --append", file=sys.stderr)
            sys.exit(1)
        append_entry(
            args.job,
            domain=args.domain,
            insight=args.insight,
            source=args.source,
            verified=args.verified,
        )
        print(f"Appended insight to {args.job}.yaml")
        return

    parser.print_help()


if __name__ == "__main__":
    main()
