#!/usr/bin/env python3
"""odysseus-preset — shell wrapper for AI system-prompt presets.

Presets are name → {name, temperature, system_prompt} in
`data/presets.json`. Used by the chat UI's preset picker.

    odysseus-preset list
    odysseus-preset get NAME
    odysseus-preset set NAME --temperature 0.7 --prompt "You are..."
    odysseus-preset set NAME --prompt-file ./prompt.md
    odysseus-preset delete NAME
"""

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, sys
from pathlib import Path

_PATH = _REPO_ROOT / "data" / "presets.json"


def _load() -> dict:
    if not _PATH.exists():
        return {}
    try:
        data = json.loads(_PATH.read_text())
    except json.JSONDecodeError as e:
        fail(f"presets.json corrupt: {e}")
    if not isinstance(data, dict):
        fail("presets.json corrupt: expected an object")
    return data


def _save(data: dict) -> None:
    _PATH.parent.mkdir(parents=True, exist_ok=True)
    # Backup; same pattern as cookbook state-set.
    if _PATH.exists():
        try:
            _PATH.with_suffix(_PATH.suffix + ".bak").write_bytes(_PATH.read_bytes())
        except Exception:
            pass
    tmp = _PATH.with_suffix(_PATH.suffix + ".tmp")
    tmp.write_text(json.dumps(data, indent=2, ensure_ascii=False))
    tmp.replace(_PATH)


def _entry_or_fail(presets: dict, name: str) -> dict:
    if name not in presets:
        fail(f"no preset named {name!r}")
    entry = presets[name]
    if not isinstance(entry, dict):
        fail(f"preset {name!r} is corrupt: expected an object")
    return entry


def cmd_list(args):
    presets = _load()
    rows = []
    for key, val in sorted(presets.items()):
        if not isinstance(val, dict):
            continue
        rows.append({
            "id": key,
            "name": val.get("name") or key,
            "temperature": val.get("temperature"),
            "prompt_length": len(val.get("system_prompt") or ""),
        })
    emit(rows, args)


def cmd_get(args):
    presets = _load()
    emit({"id": args.name, **_entry_or_fail(presets, args.name)}, args)


def cmd_set(args):
    prompt = args.prompt
    if args.prompt_file:
        prompt = Path(args.prompt_file).read_text()
    if prompt is None and args.temperature is None:
        fail("nothing to set — pass --prompt, --prompt-file, or --temperature")
    presets = _load()
    current = presets.get(args.name)
    entry = dict(current) if isinstance(current, dict) else {}
    entry.setdefault("name", args.name)
    if prompt is not None:
        entry["system_prompt"] = prompt
    if args.temperature is not None:
        entry["temperature"] = args.temperature
    if args.display_name:
        entry["name"] = args.display_name
    presets[args.name] = entry
    _save(presets)
    emit({"ok": True, "id": args.name, "entry": entry}, args)


def cmd_delete(args):
    presets = _load()
    snap = _entry_or_fail(presets, args.name)
    presets.pop(args.name)
    _save(presets)
    emit({"ok": True, "deleted": {"id": args.name, **snap}}, args)


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

    pl = sub.add_parser("list", parents=[common])
    pl.set_defaults(func=cmd_list)

    pg = sub.add_parser("get", parents=[common])
    pg.add_argument("name")
    pg.set_defaults(func=cmd_get)

    ps = sub.add_parser("set", parents=[common])
    ps.add_argument("name")
    ps.add_argument("--prompt", help="literal system prompt text")
    ps.add_argument("--prompt-file", help="path to read prompt from")
    ps.add_argument("--temperature", type=float)
    ps.add_argument("--display-name")
    ps.set_defaults(func=cmd_set)

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

    return p


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