#!/usr/bin/env python3
"""ASUCB Decision Logger — structured per-course decision changelog."""

import argparse
import json
import logging
import os
import sys
from datetime import datetime, timezone

sys.path.insert(0, os.path.dirname(__file__))
from canvas_api import get_plugin_root, validate_course_id


VALID_DECISION_TYPES = ["design", "instructor", "scope", "pivot", "tooling"]


def _decisions_path(course_id):
    course_dir = os.path.join(get_plugin_root(), "courses", str(course_id))
    os.makedirs(course_dir, exist_ok=True)
    return os.path.join(course_dir, "decisions.jsonl")


def _now_iso():
    return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")


def _read_jsonl(path):
    if not os.path.exists(path):
        return []
    records = []
    with open(path, "r", encoding="utf-8") as f:
        for line_num, line in enumerate(f, start=1):
            line = line.strip()
            if not line:
                continue
            try:
                records.append(json.loads(line))
            except json.JSONDecodeError as exc:
                logging.warning("Failed to parse %s:%s: %s", path, line_num, exc)
    return records


def _normalize_alternatives(value):
    if value in ("", None):
        return None
    if isinstance(value, list):
        return value
    raise ValueError("alternatives must be null or a list")


def build_record(course_id, payload):
    decision_type = payload.get("decision_type")
    if decision_type not in VALID_DECISION_TYPES:
        valid = ", ".join(VALID_DECISION_TYPES)
        raise ValueError(f"Invalid decision_type {decision_type!r}; must be one of: {valid}")

    title = str(payload.get("title", "")).strip()
    rationale = str(payload.get("rationale", "")).strip()
    if not title or not rationale:
        raise ValueError("title and rationale are required")

    return {
        "timestamp": _now_iso(),
        "course_id": str(course_id),
        "decision_type": decision_type,
        "title": title,
        "rationale": rationale,
        "alternatives": _normalize_alternatives(payload.get("alternatives")),
        "who": payload.get("who") or None,
        "source": payload.get("source") or None,
        "status": "active",
    }


def append_decision(course_id, payload, path=None):
    path = path or _decisions_path(course_id)
    record = build_record(course_id, payload)
    os.makedirs(os.path.dirname(path), exist_ok=True)
    with open(path, "a", encoding="utf-8") as f:
        f.write(json.dumps(record, ensure_ascii=False) + "\n")
    return {"ok": True, "path": path, "record": record}


def _prompt_payload():
    decision_type = input("decision_type: ").strip()
    title = input("title: ").strip()
    rationale = input("rationale: ").strip()
    alternatives = input("alternatives (JSON list or blank): ").strip()
    return {
        "decision_type": decision_type,
        "title": title,
        "rationale": rationale,
        "alternatives": json.loads(alternatives) if alternatives else None,
        "who": input("who (blank ok): ").strip() or None,
        "source": input("source (blank ok): ").strip() or None,
    }


def _read_payload():
    raw = "" if sys.stdin.isatty() else sys.stdin.read().strip()
    if not raw:
        return _prompt_payload()
    payload = json.loads(raw)
    if not isinstance(payload, dict):
        raise ValueError("stdin JSON must be an object")
    return payload


def _filtered(records, args):
    if args.type:
        records = [r for r in records if r.get("decision_type") == args.type]
    if args.search:
        query = args.query.lower()
        records = [
            r for r in records
            if query in json.dumps(r, ensure_ascii=False).lower()
        ]
    if args.limit is not None:
        records = records[-max(args.limit, 0):]
    return records


def main():
    parser = argparse.ArgumentParser(description="ASUCB Decision Logger")
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument("--append", action="store_true", help="Append a decision")
    group.add_argument("--list", action="store_true", help="List decisions")
    group.add_argument("--search", action="store_true", help="Search decisions")
    group.add_argument("--count", action="store_true", help="Count decisions")
    parser.add_argument("--course-id", required=True, help="Canvas course ID")
    parser.add_argument("--limit", type=int, help="Limit list results")
    parser.add_argument("--type", choices=VALID_DECISION_TYPES, help="Filter by type")
    parser.add_argument("--query", help="Search query")
    parser.add_argument("--path", help=argparse.SUPPRESS)
    # Non-interactive flags for --append (subagent/CI use)
    parser.add_argument("--decision-type", dest="decision_type",
                        choices=["design", "instructor", "scope", "pivot", "tooling"],
                        help="Decision type (skips interactive prompt)")
    parser.add_argument("--title", dest="flag_title", help="Decision title")
    parser.add_argument("--rationale", dest="flag_rationale", help="Decision rationale")
    parser.add_argument("--alternatives", dest="flag_alternatives", help="Alternatives considered")
    parser.add_argument("--who", dest="flag_who", help="Decision owner")
    parser.add_argument("--source", dest="flag_source", help="Source reference")
    args = parser.parse_args()
    if getattr(args, 'course_id', None) is not None:
        try:
            args.course_id = validate_course_id(args.course_id)
        except ValueError as exc:
            parser.error(str(exc))

    path = args.path or _decisions_path(args.course_id)
    try:
        if args.append:
            # Non-interactive path: all required flags provided
            if args.decision_type and args.flag_title and args.flag_rationale:
                payload = {
                    "decision_type": args.decision_type,
                    "title": args.flag_title,
                    "rationale": args.flag_rationale,
                }
                if args.flag_alternatives:
                    payload["alternatives"] = args.flag_alternatives
                if args.flag_who:
                    payload["who"] = args.flag_who
                if args.flag_source:
                    payload["source"] = args.flag_source
            elif (args.decision_type or args.flag_title or args.flag_rationale) \
                    and not sys.stdin.isatty():
                # Partial flags in non-TTY: error
                parser.exit(1, "decision_logger: --append requires --decision-type,"
                            " --title, and --rationale when not running interactively.\n")
            else:
                payload = _read_payload()
            print(json.dumps(
                append_decision(args.course_id, payload, path),
                indent=2,
            ))
            return
        if args.search and not args.query:
            parser.error("--search requires --query")
        records = _filtered(_read_jsonl(path), args)
        key = "count" if args.count else "decisions"
        value = len(records) if args.count else records
        print(json.dumps({"ok": True, key: value}, indent=2))
    except (OSError, ValueError, json.JSONDecodeError) as exc:
        parser.exit(2, f"decision_logger: error: {exc}\n")


if __name__ == "__main__":
    main()
