#!/usr/bin/env python3
"""odysseus-skills — shell wrapper for AI skill scripts.

Skills are `SKILL.md` files under `data/skills/<category>/<name>/`. Each
captures a reusable how-to the assistant has learned. This CLI lets you
list, inspect, and prune them from the shell.

    odysseus-skills list [--category general]
    odysseus-skills show SKILL_NAME
    odysseus-skills categories
    odysseus-skills delete SKILL_NAME
    odysseus-skills export SKILL_NAME --raw      # full SKILL.md to stdout
"""

from __future__ import annotations
import sys
import os, sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "_lib"))
from cli import quiet_logs, emit, fail, common_parser, run, REPO_ROOT as _REPO_ROOT
quiet_logs()

import argparse, json, logging, os, shutil, sys
from pathlib import Path

try:
    from services.memory.skills import SkillsManager
    quiet_logs()
except ModuleNotFoundError as e:
    sys.stderr.write(f"error: {e}\nhint: run from repo root with venv active.\n")
    sys.exit(2)


_DATA_DIR = str(_REPO_ROOT / "data")
_mgr: SkillsManager | None = None


def _manager() -> SkillsManager:
    global _mgr
    if _mgr is None:
        _mgr = SkillsManager(_DATA_DIR)
    return _mgr


def _preview_text(value, limit: int = 200) -> str:
    """Truncated preview of a text field, tolerant of non-string values.

    A skill whose ``description`` is a non-string (e.g. a number from a
    hand-edited/legacy store) would crash ``(value or "")[:200]`` with a
    TypeError; coerce non-strings to "" instead.
    """
    text = value if isinstance(value, str) else ""
    return text[:limit]


def _skill_entries(skills):
    return [s for s in skills or [] if isinstance(s, dict)]


def _summary(skill: dict) -> dict:
    return {
        "name": skill.get("name", ""),
        "category": skill.get("category", "general"),
        "description": _preview_text(skill.get("description")),
        "status": skill.get("status", ""),
        "uses": skill.get("uses", 0),
        "last_used": skill.get("last_used") or "",
        "tags": skill.get("tags") or [],
    }


def cmd_list(args):
    out = _skill_entries(_manager().load_all())
    if args.category:
        out = [s for s in out if (s.get("category") or "general") == args.category]
    out.sort(key=lambda s: (-int(s.get("uses") or 0), s.get("name", "")))
    emit([_summary(s) for s in out[: args.limit]], args)


def cmd_show(args):
    for s in _skill_entries(_manager().load_all()):
        if s.get("name") == args.name:
            emit(s, args)
            return
    fail(f"no skill named {args.name!r}")


def cmd_categories(args):
    counts: dict[str, int] = {}
    for s in _skill_entries(_manager().load_all()):
        c = s.get("category") or "general"
        counts[c] = counts.get(c, 0) + 1
    emit([{"category": c, "count": n} for c, n in sorted(counts.items())], args)


def cmd_delete(args):
    # Locate the skill's directory and rm -rf it.
    skills_root = Path(_DATA_DIR) / "skills"
    for s in _skill_entries(_manager().load_all()):
        if s.get("name") != args.name:
            continue
        cat = s.get("category") or "general"
        path = skills_root / cat / s["name"]
        if path.is_dir():
            shutil.rmtree(path)
            emit({"ok": True, "deleted": s.get("name"), "path": str(path)}, args)
            return
        fail(f"skill record found but directory missing: {path}")
    fail(f"no skill named {args.name!r}")


def cmd_export(args):
    for s in _skill_entries(_manager().load_all()):
        if s.get("name") != args.name:
            continue
        cat = s.get("category") or "general"
        md_path = Path(_DATA_DIR) / "skills" / cat / args.name / "SKILL.md"
        if not md_path.exists():
            fail(f"SKILL.md missing for {args.name!r}")
        if args.raw:
            sys.stdout.write(md_path.read_text())
            return
        emit({
            "name": args.name,
            "category": cat,
            "path": str(md_path),
            "content": md_path.read_text(),
        }, args)
        return
    fail(f"no skill named {args.name!r}")


def _build_parser():
    common = argparse.ArgumentParser(add_help=False)
    common.add_argument("--pretty", action="store_true")
    p = argparse.ArgumentParser(prog="odysseus-skills", parents=[common])
    sub = p.add_subparsers(dest="cmd", required=True)

    pl = sub.add_parser("list", parents=[common])
    pl.add_argument("--category")
    pl.add_argument("--limit", type=int, default=100)
    pl.set_defaults(func=cmd_list)

    psh = sub.add_parser("show", parents=[common])
    psh.add_argument("name")
    psh.set_defaults(func=cmd_show)

    pc = sub.add_parser("categories", parents=[common])
    pc.set_defaults(func=cmd_categories)

    pd = sub.add_parser("delete", parents=[common])
    pd.add_argument("name")
    pd.set_defaults(func=cmd_delete)

    pe = sub.add_parser("export", parents=[common])
    pe.add_argument("name")
    pe.add_argument("--raw", action="store_true", help="write raw SKILL.md to stdout")
    pe.set_defaults(func=cmd_export)

    return p


if __name__ == "__main__":
    sys.exit(run(_build_parser()))
