#!/usr/bin/env python3
"""Convert Claire's first-pass evidence CSV into the classifier training schema."""
from __future__ import annotations

import argparse
import csv
import re
from pathlib import Path


FIELDNAMES = [
    "id",
    "text",
    "overall_status",
    "action_state",
    "signals",
    "source",
    "rationale",
    "reviewed",
]


DOCUMENT_RE = re.compile(
    r"\b(?:plan|manual|label|labels|spreadsheet|file|doc|docs|document|report|minutes|sow|scope|standard|standards|"
    r"attestation|code of conduct|tracker|sharepoint|invoice|email|task list|risk mgmt|risk management)\b",
    re.I,
)
REGULATORY_RE = re.compile(r"\b(?:hpra|eudamed|udamed|udi|mdr|ppe|importer|authorised rep|legal manufacturer|competent authority|registration)\b", re.I)
PROCESS_RE = re.compile(r"\b(?:workflow|process|sequence|scheduled|planning meeting|barcoding|operations|systems|warehouse|shipping|picking|packing)\b", re.I)
RISK_RE = re.compile(r"\b(?:risk|dependency|depends|issue|block|gap|missing|concern|residual|assessment|cybersecurity)\b", re.I)
DECISION_RE = re.compile(r"\b(?:decided|agreed|approved|instructed|considered sufficient|confirmed)\b", re.I)


def truthy(value: str) -> bool:
    return str(value or "").strip().lower() in {"1", "true", "yes", "y"}


def normalise_signal(value: str) -> str:
    return str(value or "").strip().lower().replace(" ", "_").replace("-", "_")


def infer_overall(row: dict[str, str]) -> str:
    label = normalise_signal(row.get("primary_label", ""))
    text = row.get("text", "")
    if label == "confirmed_action":
        if REGULATORY_RE.search(text):
            return "regulatory_obligation"
        if DOCUMENT_RE.search(text):
            return "document_control_task"
        if RISK_RE.search(text) and DOCUMENT_RE.search(text):
            return "document_control_task"
        return "action_commitment"
    if label == "completed_history":
        if DOCUMENT_RE.search(text):
            return "background_context"
        return "background_context"
    if label == "hypothetical":
        return "discussion_topic"
    if label == "regulatory_language":
        return "regulatory_obligation"
    if label == "risk_dependency":
        return "risk_dependency"
    if label == "possible_action":
        if DOCUMENT_RE.search(text):
            return "document_control_task"
        if RISK_RE.search(text):
            return "risk_dependency"
        return "action_commitment"
    if label == "document_record":
        return "document_control_task"
    if label == "process_sequence":
        return "process_overview"
    if label == "decision":
        return "decision_agreement"
    if label == "deliverable_object":
        return "discussion_topic"
    if label == "low_value_noise":
        return "low_value_noise"
    if PROCESS_RE.search(text):
        return "process_overview"
    if REGULATORY_RE.search(text):
        return "regulatory_obligation"
    if RISK_RE.search(text):
        return "risk_dependency"
    return "discussion_topic"


def infer_action_state(row: dict[str, str]) -> str:
    label = normalise_signal(row.get("primary_label", ""))
    if label == "completed_history" or normalise_signal(row.get("temporal_status", "")) == "completed":
        return "completed_history"
    if label == "confirmed_action" or truthy(row.get("is_action", "")):
        return "confirmed_action"
    if label == "possible_action":
        return "possible_action"
    if normalise_signal(row.get("modality", "")) in {"hedged", "conditional", "planned", "scheduled"}:
        return "not_action" if label == "hypothetical" else "possible_action"
    return "not_action"


def infer_signals(row: dict[str, str]) -> list[str]:
    text = row.get("text", "")
    signals: set[str] = set()
    if truthy(row.get("owner_explicit", "")) or row.get("owner", "").strip():
        signals.add("named_owner")
    if row.get("action_verb", "").strip():
        signals.add("explicit_commitment_verb")
    if row.get("deliverable_or_object", "").strip() or DOCUMENT_RE.search(text) or REGULATORY_RE.search(text):
        signals.add("deliverable_object")
    if re.search(r"\b(?:by|before|after|next|today|tomorrow|wednesday|friday|week|july|march|november|\d{1,2}(?:st|nd|rd|th)?)\b", text, re.I):
        signals.add("deadline_or_sequence")
    if normalise_signal(row.get("action_verb", "")) in {"share", "send", "followup", "upload", "get"} or re.search(r"\b(?:with|to|from)\s+[A-Z][a-z]+", text):
        signals.add("recipient_or_handover")
    if DOCUMENT_RE.search(text) or row.get("deliverable_or_object", "").strip():
        signals.add("document_or_record")
    if REGULATORY_RE.search(text):
        signals.add("regulatory_language")
    if PROCESS_RE.search(text):
        signals.add("process_sequence")
    if DECISION_RE.search(text) or normalise_signal(row.get("modality", "")) == "decided":
        signals.add("decision_language")
    if RISK_RE.search(text):
        signals.add("risk_language")
    if normalise_signal(row.get("modality", "")) in {"hedged", "conditional"} or re.search(r"\b(?:could|maybe|might|should|thinking|if)\b", text, re.I):
        signals.add("hypothetical_language")
    if normalise_signal(row.get("temporal_status", "")) == "completed" or re.search(r"\b(?:already|reviewed|completed|sent|paid|was|were|has been|had been)\b", text, re.I):
        signals.add("completed_tense")
    return sorted(signals)


def convert_row(row: dict[str, str], index: int) -> dict[str, str]:
    source = row.get("source_file", "").strip() or row.get("example_type", "").strip() or "first_pass_upload"
    notes = row.get("notes", "").strip()
    metadata = []
    for key in ("primary_label", "temporal_status", "modality", "owner", "action_verb", "deliverable_or_object", "contrast_group", "example_type"):
        value = row.get(key, "").strip()
        if value:
            metadata.append(f"{key}={value}")
    rationale = notes
    if metadata:
        rationale = f"{rationale} [{' ; '.join(metadata)}]".strip()
    return {
        "id": f"mme_upload_001_{index:03d}",
        "text": row.get("text", "").strip(),
        "overall_status": infer_overall(row),
        "action_state": infer_action_state(row),
        "signals": "|".join(infer_signals(row)),
        "source": source,
        "rationale": rationale,
        "reviewed": "true",
    }


def read_training(path: Path) -> list[dict[str, str]]:
    with path.open(newline="", encoding="utf-8-sig") as handle:
        return list(csv.DictReader(handle))


def write_training(path: Path, rows: list[dict[str, str]]) -> None:
    with path.open("w", newline="", encoding="utf-8") as handle:
        writer = csv.DictWriter(handle, fieldnames=FIELDNAMES)
        writer.writeheader()
        writer.writerows(rows)


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--base", default="data/training_data.csv")
    parser.add_argument("--upload", default="source_uploads/meeting_minutes_evidence_training_first_pass.csv")
    parser.add_argument("--output", default="data/training_data_augmented_first_pass.csv")
    args = parser.parse_args()

    base = read_training(Path(args.base))
    with Path(args.upload).open(newline="", encoding="utf-8-sig") as handle:
        uploaded = list(csv.DictReader(handle))
    converted = [convert_row(row, idx) for idx, row in enumerate(uploaded, start=1) if row.get("text", "").strip()]

    seen = {row["text"].strip().lower() for row in base}
    additions = []
    for row in converted:
        key = row["text"].strip().lower()
        if key and key not in seen:
            additions.append(row)
            seen.add(key)

    merged = base + additions
    write_training(Path(args.output), merged)
    print(f"base_rows={len(base)} upload_rows={len(uploaded)} additions={len(additions)} output_rows={len(merged)}")
    return 0


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