#!/usr/bin/env python3
"""AKP-to-Linear — Scannt Video-Notes mit prioritaet: P0|P1 ohne linear:-Field
und legt KAR-Issues an. KAR-350 Tier-1 G1-5.

Run: python3 aria-akp-to-linear.py [--dry-run] [--max N]

!!! DEAKTIVIERT 2026-05-24 (Kais-Anweisung, KAR-Backlog-Reduktionsplan) !!!
Der systemd-Timer `aria-akp-to-linear.timer` wurde disabled. Grund: das
Auto-Promoting machte aus JEDEM relevanten Video ein [Video]-KAR → 124 von 318
offenen KARs (39%) waren Video-Watch-Items statt Action-Items. Policy jetzt:
Videos bleiben Brain-Notes (Deep-Process) + AKP-Briefing — KEIN Auto-KAR mehr.
Wenn aus einem Video ein echtes Action-Item entsteht, legt Aria es BEWUSST an
(nicht auto). Skript bleibt als Tooling erhalten; NICHT per Timer reaktivieren
ohne erneute Policy-Entscheidung. Siehe 02-Wissen/kar-backlog-reduktionsplan-2026-05-24.md.
"""
from __future__ import annotations
import sys as _sys
_sys.path.insert(0, "/root/aria/lib")
from aria_logging import get_logger as _get_logger
_log = _get_logger("aria-akp-to-linear")
from aria_audit import audit as _audit  # KAR-220 P2

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

INBOX = Path("/root/aria/brain/00-Inbox/Videos")
STATE = Path("/root/aria/state/akp-to-linear.json")
API_KEY = os.environ.get("LINEAR_API_KEY")
URL = "https://api.linear.app/graphql"


def load_state() -> dict:
    if STATE.exists():
        try:
            return json.loads(STATE.read_text())
        except Exception:
            pass
    return {"processed": []}


def save_state(s: dict) -> None:
    STATE.parent.mkdir(parents=True, exist_ok=True)
    STATE.write_text(json.dumps(s, indent=2))


def parse_frontmatter(text: str) -> dict:
    if not text.startswith("---"):
        return {}
    end = text.find("\n---", 4)
    if end < 0:
        return {}
    fm = text[4:end]
    out = {}
    for line in fm.splitlines():
        m = re.match(r"^(\w+):\s*(.*)$", line)
        if m:
            out[m.group(1)] = m.group(2).strip().strip("\"'")
    return out


def get_kar_team_id(headers: dict) -> str:
    q = "query { teams { nodes { id key } } }"
    r = requests.post(URL, json={"query": q}, headers=headers).json()
    for t in r["data"]["teams"]["nodes"]:
        if t["key"] == "KAR":
            return t["id"]
    raise SystemExit("KAR team not found")


def get_backlog_state_id(headers: dict, team_id: str) -> str:
    q = "query($id: String!) { team(id: $id) { states { nodes { id name type } } } }"
    r = requests.post(URL, json={"query": q, "variables": {"id": team_id}}, headers=headers).json()
    for s in r["data"]["team"]["states"]["nodes"]:
        if s["type"] == "backlog":
            return s["id"]
    raise SystemExit("backlog state not found")


def create_issue(headers: dict, team_id: str, state_id: str, title: str, body: str, priority: int = 3) -> dict:
    mut = """mutation($title: String!, $body: String!, $teamId: String!, $stateId: String!, $prio: Int!) {
      issueCreate(input: {title: $title, description: $body, teamId: $teamId, stateId: $stateId, priority: $prio}) {
        success issue { identifier url id }
      }
    }"""
    r = requests.post(URL, json={"query": mut, "variables": {"title": title, "body": body, "teamId": team_id, "stateId": state_id, "prio": priority}}, headers=headers).json()
    if r.get("data", {}).get("issueCreate", {}).get("success"):
        return r["data"]["issueCreate"]["issue"]
    raise RuntimeError(f"Linear create failed: {r}")


def build_body(note_path: Path, fm: dict) -> str:
    rel = note_path.relative_to(Path("/root/aria/brain"))
    body = [
        f"## Auto-Generated from Video-Note",
        f"",
        f"**Source:** [{fm.get('source','')}]({fm.get('source','')})",
        f"**Channel:** {fm.get('channel','unknown')}",
        f"**Dauer:** {fm.get('duration_seconds','?')} sec",
        f"**Thema:** {fm.get('thema','')}",
        f"**Nutzen:** {fm.get('nutzen','')}",
        f"**Umsetzungsidee:** {fm.get('umsetzungsidee','')}",
        f"**Confidence:** {fm.get('confidence','?')}",
        f"**Klassifikation:** {fm.get('klassifikation','')}",
        f"",
        f"## Brain-Note",
        f"",
        f"`brain/{rel}`",
        f"",
        f"Volle Praxisanalyse + Kritik in Brain-Note.",
        f"",
        f"## Quelle",
        f"",
        f"AKP-Pipeline (KAR-74), auto-promoted via aria-akp-to-linear.py.",
        f"Created: {datetime.now(timezone.utc).isoformat()}",
    ]
    return "\n".join(body)


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--dry-run", action="store_true")
    ap.add_argument("--max", type=int, default=50, help="Max new issues per run (raised from 20 on 2026-05-21)")
    ap.add_argument("--include-p2", action="store_true", help="Include P2 priority (default: P0+P1 only)")
    args = ap.parse_args()

    if not API_KEY:
        print("[akp-to-linear] LINEAR_API_KEY missing")
        return 1

    headers = {"Authorization": API_KEY}
    state = load_state()
    processed_ids = set(state.get("processed", []))

    candidates = []
    for p in sorted(INBOX.glob("*.md")):
        if "pending-synthesis" in p.name:
            continue
        if p.name in processed_ids:
            continue
        text = p.read_text(errors="replace")
        fm = parse_frontmatter(text)
        prio = fm.get("prioritaet", "")
        allowed = ("P0", "P1", "P2") if args.include_p2 else ("P0", "P1")
        if prio not in allowed:
            continue
        if fm.get("linear"):
            continue
        if "klassifikation" in fm and fm["klassifikation"] in ("Ignore", "Brain-Entry"):
            continue
        candidates.append((p, fm))

    print(f"[akp-to-linear] candidates: {len(candidates)}")
    if not candidates:
        return 0

    if args.dry_run:
        for p, fm in candidates[:args.max]:
            print(f"  DRY  {p.name} :: {fm.get('title', p.name)} :: {fm.get('prioritaet')}")
        return 0

    team_id = get_kar_team_id(headers)
    state_id = get_backlog_state_id(headers, team_id)
    created = 0
    for p, fm in candidates[: args.max]:
        try:
            title = f"[Video] {fm.get('title', p.stem)[:120]}"
            body = build_body(p, fm)
            prio_map = {"P0": 1, "P1": 2, "P2": 3, "P3": 4}
            issue = create_issue(headers, team_id, state_id, title, body, priority=prio_map.get(fm.get("prioritaet"), 3))
            print(f"  OK   {issue['identifier']} <- {p.name}")
            processed_ids.add(p.name)
            # update note frontmatter with linear: ID
            new_text = p.read_text(errors="replace")
            if "linear:" not in new_text.split("---")[1]:
                new_text = re.sub(r"(\nstatus: aktiv)", f"\\1\nlinear: {issue['identifier']}", new_text, count=1)
                p.write_text(new_text)
            created += 1
        except Exception as e:
            print(f"  ERR  {p.name}: {e}")
    state["processed"] = sorted(processed_ids)
    save_state(state)
    print(f"[akp-to-linear] created {created} new issues")


if __name__ == "__main__":
    _log.event("script_start")
    sys.exit(main() or 0)
