#!/usr/bin/env python3
"""Maintain the split task index.

The canonical source is one TSV file per task under
control/task-index/items/. The aggregate tasks.tsv is regenerated for fast
search and compatibility with existing commands.
"""

from __future__ import annotations

import argparse
import csv
import re
from pathlib import Path


ROOT = Path(__file__).resolve().parents[2]
TASK_INDEX_DIR = ROOT / "control" / "task-index"
TASKS_TSV = TASK_INDEX_DIR / "tasks.tsv"
ITEMS_DIR = TASK_INDEX_DIR / "items"

HEADER = [
    "task_id",
    "title",
    "source",
    "target_repo",
    "target_branch",
    "task_branch",
    "commit",
    "mr_url",
    "status",
    "evidence_path",
    "skills",
    "tags",
    "updated_at",
]


SAFE_NAME_RE = re.compile(r"[^A-Za-z0-9._-]+")


def safe_filename(task_id: str) -> str:
    return SAFE_NAME_RE.sub("-", task_id).strip("-") + ".tsv"


def read_tsv(path: Path) -> list[dict[str, str]]:
    with path.open(newline="", encoding="utf-8") as handle:
        reader = csv.DictReader(handle, delimiter="\t")
        if reader.fieldnames != HEADER:
            raise SystemExit(f"{path}: unexpected header {reader.fieldnames!r}")
        return list(reader)


def write_tsv(path: Path, rows: list[dict[str, str]]) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    with path.open("w", newline="", encoding="utf-8") as handle:
        writer = csv.DictWriter(handle, fieldnames=HEADER, delimiter="\t", lineterminator="\n")
        writer.writeheader()
        writer.writerows(rows)


def split() -> None:
    rows = read_tsv(TASKS_TSV)
    seen: set[str] = set()
    ITEMS_DIR.mkdir(parents=True, exist_ok=True)
    for row in rows:
        task_id = row["task_id"]
        if task_id in seen:
            raise SystemExit(f"duplicate task_id in {TASKS_TSV}: {task_id}")
        seen.add(task_id)
        write_tsv(ITEMS_DIR / safe_filename(task_id), [row])
    print(f"split {len(rows)} task rows into {ITEMS_DIR.relative_to(ROOT)}")


def build() -> None:
    rows: list[dict[str, str]] = []
    seen: set[str] = set()
    for path in sorted(ITEMS_DIR.glob("*.tsv")):
        item_rows = read_tsv(path)
        if len(item_rows) != 1:
            raise SystemExit(f"{path}: expected exactly one data row")
        row = item_rows[0]
        task_id = row["task_id"]
        if task_id in seen:
            raise SystemExit(f"duplicate task_id in items: {task_id}")
        seen.add(task_id)
        rows.append(row)
    write_tsv(TASKS_TSV, rows)
    print(f"built {TASKS_TSV.relative_to(ROOT)} from {len(rows)} item files")


def validate() -> None:
    rows = read_tsv(TASKS_TSV)
    item_rows = []
    seen: set[str] = set()
    for path in sorted(ITEMS_DIR.glob("*.tsv")):
        records = read_tsv(path)
        if len(records) != 1:
            raise SystemExit(f"{path}: expected exactly one data row")
        task_id = records[0]["task_id"]
        if task_id in seen:
            raise SystemExit(f"duplicate task_id in items: {task_id}")
        seen.add(task_id)
        item_rows.extend(records)
    if rows != item_rows:
        raise SystemExit("tasks.tsv is out of date; run: python3 control/scripts/task_index.py build")
    print(f"validated {len(rows)} task rows")


def main() -> None:
    parser = argparse.ArgumentParser(description="Maintain control/task-index split files")
    parser.add_argument("command", choices=["split", "build", "validate"])
    args = parser.parse_args()

    if args.command == "split":
        split()
    elif args.command == "build":
        build()
    else:
        validate()


if __name__ == "__main__":
    main()
