#!/usr/bin/env python3
"""Generate a per-course handoff brief from course memory and local logs."""

import argparse
import glob
import json
import os
import re
import sys
from datetime import date, datetime, timezone
from pathlib import Path

sys.path.insert(0, os.path.dirname(__file__))
try:
    from dotenv import load_dotenv
    load_dotenv(os.path.join(os.path.dirname(os.path.dirname(__file__)), ".env"))
except ImportError:
    pass
from canvas_api import get_plugin_root, validate_course_id


SECTION_ORDER = [
    "Course Meta",
    "Faculty Preferences",
    "Design Decisions",
    "Current State",
    "Outstanding Pending",
    "Recent Decisions Log",
    "Recent Timeline Events",
    "Build History",
    "Notes",
    "Faculty Contact Log",
    "Quick Links",
]


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


def _parse_frontmatter(text):
    meta = {}
    body = text
    if text.startswith("---"):
        end = text.find("\n---", 3)
        if end != -1:
            for line in text[3:end].strip().splitlines():
                if ":" in line:
                    _pf_key = line.split(":", 1)
                    key = _pf_key[0].strip() if _pf_key else ""
                    value = _pf_key[1].strip() if len(_pf_key) > 1 else ""
                    meta[key.strip()] = value.strip().strip('"').strip("'")
            body = text[end + 4:].lstrip("\n")
    return meta, body


def _split_sections(body):
    sections = {}
    current = None
    lines = []
    for line in body.splitlines():
        match = re.match(r"^##\s+(.+?)\s*$", line)
        if match:
            if current is not None:
                sections.setdefault(current, []).append("\n".join(lines).strip())
            current = match.group(1).strip()
            lines = []
        elif current is not None:
            lines.append(line)
    if current is not None:
        sections.setdefault(current, []).append("\n".join(lines).strip())
    return {k: "\n\n".join(v).strip() for k, v in sections.items()}


def _matching_sections(body, prefix):
    blocks = []
    current = None
    lines = []
    for line in body.splitlines():
        match = re.match(r"^##\s+(.+?)\s*$", line)
        if match:
            if current and current.lower().startswith(prefix.lower()):
                blocks.append("\n".join(lines).strip())
            current = match.group(1).strip()
            lines = []
        elif current is not None:
            lines.append(line)
    if current and current.lower().startswith(prefix.lower()):
        blocks.append("\n".join(lines).strip())
    return "\n\n".join(b for b in blocks if b).strip()


def _load_json(path):
    if not path.exists():
        return {}
    with open(path, "r", encoding="utf-8") as f:
        return json.load(f)


def _read_jsonl(path, limit=10):
    if not path.exists():
        return None
    records = []
    with open(path, "r", encoding="utf-8") as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            try:
                records.append(json.loads(line))
            except json.JSONDecodeError:
                continue
    return records[-limit:]


def _format_records(records, kind):
    if records is None:
        return f"_{kind} log not found._"
    if not records:
        return f"_(no {kind.lower()} records)_"

    lines = []
    for record in records:
        timestamp = record.get("timestamp", "unknown time")
        if kind == "Decisions":
            label = record.get("decision_type", "decision")
            title = record.get("title", "(untitled)")
        else:
            label = record.get("event_type", "event")
            payload = record.get("payload", {})
            title = payload.get("title") if isinstance(payload, dict) else None
            title = title or json.dumps(payload, ensure_ascii=False)[:120]
        lines.append(f"- **{timestamp}** [{label}] {title}")
    return "\n".join(lines)


def _course_name(config, memory_meta, course_id):
    return (
        config.get("name")
        or config.get("course")
        or memory_meta.get("course")
        or f"Course {course_id}"
    )


def _course_meta(config, memory_meta):
    if config:
        keys = ["name", "course_code", "canvas_code", "term", "session", "faculty"]
        lines = [f"- **{key}**: {config[key]}" for key in keys if config.get(key)]
        extra = sorted(k for k in config.keys() if k not in keys)
        lines.extend(f"- **{key}**: {config[key]}" for key in extra[:8])
        return "\n".join(lines) or "_No key course-config fields found._"
    if memory_meta:
        return "\n".join(f"- **{k}**: {v}" for k, v in memory_meta.items())
    return "_No course-config.json or memory frontmatter found._"


def _split_pending(pending_text):
    can_do = []
    blocked = []
    in_blocked = False
    for raw in pending_text.splitlines():
        line = raw.strip()
        if not line:
            continue
        if re.match(r"^#{1,4}\s+Blocked", line, re.IGNORECASE):
            in_blocked = True
            blocked.append(raw)
            continue
        if re.match(r"^#{1,4}\s+", line):
            in_blocked = False
        target = blocked if in_blocked or re.search(r"\bblocked\b", line, re.I) else can_do
        target.append(raw)
    return "\n".join(can_do).strip() or "_None._", "\n".join(blocked).strip() or "_None._"


def _latest_syllabus_backup(course_dir):
    patterns = [
        str(course_dir / "backups" / "syllabus_*"),
        str(course_dir / "backups" / "syllabus" / "syllabus_*"),
    ]
    matches = []
    for pattern in patterns:
        matches.extend(glob.glob(pattern))
    if not matches:
        return "_No syllabus backup found._"
    return max(matches, key=os.path.getmtime)


def _mockup_link(course_dir):
    manifest_path = course_dir / "mockup" / "manifest.json"
    if not manifest_path.exists():
        return "_No mockup manifest found._"
    manifest = _load_json(manifest_path)
    return manifest.get("url") or manifest.get("mockup_url") or str(manifest_path)


def _quick_links(course_id, course_dir):
    domain = os.environ.get("CANVAS_DOMAIN", "canvas.asu.edu")
    return "\n".join([
        f"- Canvas: https://{domain}/courses/{course_id}",
        f"- Latest syllabus backup: {_latest_syllabus_backup(course_dir)}",
        f"- Mockup: {_mockup_link(course_dir)}",
    ])


def build_handoff(course_id, handoff_date, author=""):
    root = Path(get_plugin_root())
    course_dir = root / "courses" / str(course_id)
    memory_path = course_dir / "course-memory.md"
    memory_text = memory_path.read_text(encoding="utf-8") if memory_path.exists() else ""
    memory_meta, memory_body = _parse_frontmatter(memory_text)
    memory_sections = _split_sections(memory_body)
    config = _load_json(course_dir / "course-config.json")
    course = _course_name(config, memory_meta, course_id)
    pending = _matching_sections(memory_body, "Pending")
    can_do, blocked = _split_pending(pending)

    sections = {
        "Course Meta": _course_meta(config, memory_meta),
        "Faculty Preferences": memory_sections.get("Faculty Preferences") or "_Not found._",
        "Design Decisions": memory_sections.get("Design Decisions") or "_Not found._",
        "Current State": memory_sections.get("Current State") or "_Not found._",
        "Outstanding Pending": f"### Can Do Now\n\n{can_do}\n\n### Blocked\n\n{blocked}",
        "Recent Decisions Log": _format_records(_read_jsonl(course_dir / "decisions.jsonl"), "Decisions"),
        "Recent Timeline Events": _format_records(_read_jsonl(course_dir / "timeline.jsonl"), "Timeline"),
        "Build History": memory_sections.get("Build History") or "_Not found._",
        "Notes": memory_sections.get("Notes") or "_Not found._",
        "Faculty Contact Log": "<!-- Add faculty contact notes here -->",
        "Quick Links": _quick_links(course_id, course_dir),
    }

    frontmatter = [
        "---",
        f'courseId: "{course_id}"',
        f"course: {course}",
        f"handoff_date: {handoff_date[:4]}-{handoff_date[4:6]}-{handoff_date[6:]}",
        f"generated_at: {_now_iso()}",
        f"author: {author}",
        "---",
        "",
    ]
    body = [f"# Handoff: {course}", ""]
    for heading in SECTION_ORDER:
        body.extend([f"## {heading}", "", sections[heading], ""])
    return "\n".join(frontmatter + body)


def _write_docx(markdown_text, docx_path):
    try:
        from docx import Document
    except ImportError:
        print("[generate_handoff] WARNING: python-docx not installed; emitted Markdown only.")
        return False

    doc = Document()
    for line in markdown_text.splitlines():
        if line.startswith("# "):
            doc.add_heading(line[2:], level=1)
        elif line.startswith("## "):
            doc.add_heading(line[3:], level=2)
        elif line.startswith("### "):
            doc.add_heading(line[4:], level=3)
        elif line.startswith("- "):
            doc.add_paragraph(line[2:], style="List Bullet")
        elif line.strip() and line != "---" and not re.match(r"^\w+:", line):
            doc.add_paragraph(line)
    doc.save(docx_path)
    return True


def main():
    parser = argparse.ArgumentParser(description="Generate course handoff brief")
    parser.add_argument("--course-id", required=True, help="Canvas course ID")
    parser.add_argument("--format", choices=["md", "docx", "both"], default="md")
    parser.add_argument("--date", default=date.today().strftime("%Y%m%d"),
                        help="Handoff date as YYYYMMDD")
    parser.add_argument("--author", default="")
    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))

    if not re.match(r"^\d{8}$", args.date):
        parser.error("--date must use YYYYMMDD")

    root = Path(get_plugin_root())
    out_dir = root / "courses" / str(args.course_id) / "handoffs"
    out_dir.mkdir(parents=True, exist_ok=True)
    md_path = out_dir / f"handoff_{args.date}.md"
    docx_path = out_dir / f"handoff_{args.date}.docx"
    markdown_text = build_handoff(args.course_id, args.date, args.author)
    md_path.write_text(markdown_text, encoding="utf-8")

    wrote_docx = False
    if args.format in ("docx", "both"):
        wrote_docx = _write_docx(markdown_text, docx_path)

    result = {"ok": True, "markdown": str(md_path)}
    if wrote_docx:
        result["docx"] = str(docx_path)
    print(json.dumps(result, indent=2))


if __name__ == "__main__":
    main()
