#!/usr/bin/env python3
"""Write ECHO session closeout artifacts.

Creates or updates the daily Agent Log, writes a canonical session log, and
updates the heartbeat pointer. This keeps the logging workflow explicit while
making it hard to skip after substantive memory writes.
"""

from __future__ import annotations

import argparse
import datetime as dt
import re
import sys
import tempfile
from pathlib import Path

SCRIPT_DIR = Path(__file__).resolve().parent
SKILL_DIR = SCRIPT_DIR.parent
sys.path.insert(0, str(SCRIPT_DIR))

import echo  # noqa: E402


def slugify(text: str) -> str:
    slug = re.sub(r"[^a-z0-9]+", "-", text.lower()).strip("-")
    slug = re.sub(r"-{2,}", "-", slug)
    return slug[:60].strip("-") or "session"


def now_local() -> dt.datetime:
    override = echo.today()
    current = dt.datetime.now()
    try:
        date = dt.date.fromisoformat(override)
        return current.replace(year=date.year, month=date.month, day=date.day)
    except ValueError:
        return current


def temp_file(text: str) -> str:
    handle = tempfile.NamedTemporaryFile("w", encoding="utf-8", newline="\n", delete=False)
    try:
        handle.write(text)
        handle.close()
        return handle.name
    finally:
        try:
            handle.close()
        except Exception:
            pass


def get_text(path: str) -> str | None:
    status, body = echo.request("GET", echo.vault_url(path))
    if status == 404:
        return None
    echo.check(status, body, f"get {path}")
    return body.decode("utf-8", errors="replace")


def put_text(path: str, text: str) -> None:
    echo.cmd_put(path, temp_file(text))


def patch_heading(path: str, op: str, target: str, text: str) -> None:
    echo.cmd_patch(path, op, "heading", target, temp_file(text))


def ensure_daily_note(date: str) -> str:
    path = f"journal/daily/{date}.md"
    text = get_text(path)
    if text is None:
        template_path = SKILL_DIR / "scaffold" / "templates" / "journal" / "templates" / "daily-note-template.md"
        template = template_path.read_text(encoding="utf-8")
        body = template.replace("{{date:YYYY-MM-DD}}", date).replace("{{DATE}}", date)
        put_text(path, body)
        text = body
    if not re.search(r"(?m)^## Agent Log\s*$", text):
        echo.cmd_post(path, temp_file("\n\n## Agent Log\n"))
    return path


def append_daily_log(date: str, line: str) -> str:
    path = ensure_daily_note(date)
    target = f"{date}::Agent Log"
    entry = line if line.startswith("- ") else f"- {date}: {line}"
    existing = get_text(path) or ""
    if entry in existing:
        print(f"skip: daily Agent Log already contains entry for {path}")
        return path
    patch_heading(path, "append", target, entry)
    return path


def bullet_section(items: list[str]) -> str:
    return "\n".join(f"- {item}" for item in items)


def render_session_log(args: argparse.Namespace, created: dt.datetime, session_path: str) -> str:
    iso_minute = created.strftime("%Y-%m-%dT%H:%M")
    date = created.date().isoformat()
    related = args.related or []
    notes = args.notes_read or []
    outputs = args.outputs or []
    decisions = args.decisions or []
    open_threads = args.open_threads or []
    sections = [
        "---",
        "type: session-log",
        "status: complete",
        f"created: {iso_minute}",
        f"updated: {iso_minute}",
        "tags: [agent, session, codex]",
        "agent_written: true",
        "source_notes: []",
        f"session_date: {date}",
        "client: codex",
        "---",
        "",
        "# Session Log",
        "",
        "## Goal",
        args.goal.strip(),
        "",
    ]
    if notes:
        sections.extend(["## Notes Read", bullet_section(notes), ""])
    sections.extend(["## Actions Taken", args.actions.strip(), ""])
    if decisions:
        sections.extend(["## Decisions Made", bullet_section(decisions), ""])
    if outputs:
        sections.extend(["## Outputs Created", bullet_section(outputs), ""])
    if open_threads:
        sections.extend(["## Open Threads", "\n".join(f"- [ ] {item}" for item in open_threads), ""])
    sections.extend(["## Suggested Next Step", args.next_step.strip(), ""])
    if related:
        sections.extend(["## Related", bullet_section(related), ""])
    sections.append(f"Session path: `{session_path}`")
    return "\n".join(sections).rstrip() + "\n"


def write_session_log(args: argparse.Namespace, created: dt.datetime) -> str:
    date = created.date().isoformat()
    hhmm = created.strftime("%H%M")
    slug = slugify(args.slug or args.goal)
    filename = f"{date}-{hhmm}-{slug}.md"
    if not re.match(r"^\d{4}-\d{2}-\d{2}-\d{4}-[a-z0-9-]+\.md$", filename):
        raise echo.EchoError(f"invalid session filename: {filename}", 2)
    path = f"_agent/sessions/{filename}"
    existing = get_text(path)
    if existing is None:
        put_text(path, render_session_log(args, created, path))
    else:
        print(f"skip: session log already exists at {path}")
    return path


def update_heartbeat(session_path: str, created: dt.datetime) -> None:
    stamp = created.astimezone(dt.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
    put_text("_agent/heartbeat/last-session.md", f"{session_path} @ {stamp}\n")


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(description="Write ECHO session closeout artifacts")
    parser.add_argument("--slug", help="2-5 word kebab-case-ish session topic")
    parser.add_argument("--goal", required=True)
    parser.add_argument("--actions", required=True)
    parser.add_argument("--next-step", required=True)
    parser.add_argument("--daily-line", required=True)
    parser.add_argument("--related", action="append", default=[])
    parser.add_argument("--notes-read", action="append", default=[])
    parser.add_argument("--outputs", action="append", default=[])
    parser.add_argument("--decisions", action="append", default=[])
    parser.add_argument("--open-threads", action="append", default=[])
    return parser


def main(argv: list[str] | None = None) -> int:
    args = build_parser().parse_args(argv)
    created = now_local()
    session_path = write_session_log(args, created)
    daily_path = append_daily_log(created.date().isoformat(), args.daily_line)
    update_heartbeat(session_path, created)
    print(f"ok: session log {session_path}")
    print(f"ok: daily log {daily_path}")
    print("ok: heartbeat updated")
    return 0


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