"""
classifier_export.py — ADR-0008 Phase 1 + ADR-0010 automatic retraining trigger

Exports classify_history_store to JSONL when the session count reaches
EXPORT_THRESHOLD. Each exported record is a labeled (query, type) pair.

After a successful export, triggers the Champion/Challenger retraining pipeline
(ADR-0010) in a background thread if RETRAIN_ON_EXPORT=true.

Export format (one JSON object per line):
    {"query": "60-char topic snippet", "type": "RETRIEVAL", "session_id": "abc"}

The store is flushed after export to prevent unbounded memory growth.
Files are written to CLASSIFIER_EXPORT_DIR (default: /data/classifier_labels/).
"""

import json
import logging
import os
import subprocess
import sys
import threading
from datetime import datetime, timezone
from pathlib import Path

logger = logging.getLogger(__name__)

EXPORT_THRESHOLD  = int(os.getenv("CLASSIFIER_EXPORT_THRESHOLD", "100"))
EXPORT_DIR        = Path(os.getenv("CLASSIFIER_EXPORT_DIR", "/data/classifier_labels"))
RETRAIN_ON_EXPORT = os.getenv("RETRAIN_ON_EXPORT", "false").lower() == "true"
RETRAIN_SCRIPT    = Path(os.getenv("RETRAIN_SCRIPT_PATH",
                         "/app/scripts/pipelines/classifier/retrain_pipeline.py"))

_export_lock = threading.Lock()


def maybe_export(classify_history_store: dict) -> bool:
    """Check session count and export if threshold is reached.

    Called after every request. Thread-safe. Returns True if export occurred.
    Flushes the store after a successful export.
    """
    if len(classify_history_store) < EXPORT_THRESHOLD:
        return False

    with _export_lock:
        # Re-check inside lock — another thread may have already exported
        if len(classify_history_store) < EXPORT_THRESHOLD:
            return False

        return _do_export(classify_history_store)


def _do_export(classify_history_store: dict) -> bool:
    try:
        EXPORT_DIR.mkdir(parents=True, exist_ok=True)

        ts = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
        path = EXPORT_DIR / f"classifier_labels_{ts}.jsonl"

        records = []
        for session_id, entries in classify_history_store.items():
            for entry in entries:
                records.append({
                    "query":      entry.get("topic", ""),
                    "type":       entry.get("type", ""),
                    "session_id": session_id,
                })

        with open(path, "w", encoding="utf-8") as f:
            for record in records:
                f.write(json.dumps(record, ensure_ascii=False) + "\n")

        n_sessions = len(classify_history_store)
        n_records  = len(records)

        classify_history_store.clear()

        logger.info(
            f"[classifier_export] exported {n_records} records "
            f"from {n_sessions} sessions → {path}"
        )

        if RETRAIN_ON_EXPORT:
            _trigger_retrain()

        return True

    except Exception as e:
        logger.error(f"[classifier_export] export failed: {e}", exc_info=True)
        return False


def _trigger_retrain() -> None:
    """Launch retrain_pipeline.py in a background thread.

    Runs as a subprocess so it does not block the gRPC server. The engine
    continues serving requests while retraining happens. The new model is
    loaded on the next engine restart — live reload is not performed to avoid
    serving inconsistent results mid-session.
    """
    def _run():
        if not RETRAIN_SCRIPT.exists():
            logger.warning(
                f"[classifier_export] retrain script not found at {RETRAIN_SCRIPT} "
                "— set RETRAIN_SCRIPT_PATH or disable RETRAIN_ON_EXPORT"
            )
            return
        try:
            logger.info(f"[classifier_export] launching retraining pipeline → {RETRAIN_SCRIPT}")
            result = subprocess.run(
                [sys.executable, str(RETRAIN_SCRIPT)],
                capture_output=True,
                text=True,
                timeout=600,  # 10 min hard limit
            )
            if result.returncode == 0:
                logger.info("[classifier_export] retraining completed — restart engine to load new model")
            else:
                logger.error(
                    f"[classifier_export] retraining failed (exit {result.returncode})\n"
                    f"{result.stderr[-500:]}"
                )
        except subprocess.TimeoutExpired:
            logger.error("[classifier_export] retraining timed out after 600s")
        except Exception as e:
            logger.error(f"[classifier_export] retraining error: {e}", exc_info=True)

    threading.Thread(target=_run, daemon=True, name="retrain-pipeline").start()


def force_export(classify_history_store: dict) -> bool:
    """Export immediately regardless of threshold. Useful for shutdown hooks."""
    if not classify_history_store:
        logger.info("[classifier_export] nothing to export")
        return False
    with _export_lock:
        return _do_export(classify_history_store)
