#!/usr/bin/env python3
"""Pipeline Orchestrator CLI — manage pipeline runs and state.

This script provides state management for the 6-stage pipeline.
Claude orchestrates the actual stages via slash commands.

Usage:
    python pipeline.py init --input "prompt engineering" --type topic [--options '{"max_sources": 10}']
    python pipeline.py advance --run-id 1 --stage crawling [--stats '{"pages_crawled": 45}']
    python pipeline.py pause --run-id 1 --error "Crawl timeout" --stage crawling
    python pipeline.py resume --run-id 1
    python pipeline.py complete --run-id 1 [--export-path ~/reference-library/exports/]
    python pipeline.py status [--run-id 1]
    python pipeline.py track-iteration --run-id 1 --doc-id 42 --action refactor
"""

import json
import sys
from datetime import datetime
from pathlib import Path

import click
from rich.console import Console
from rich.table import Table

from refcurator.db import db_session
from refcurator.config import get_pipeline_config

console = Console()

# QA loop limits
MAX_REFACTOR = 3
MAX_DEEP_RESEARCH = 2
MAX_TOTAL = 5


@click.group()
def cli():
    """Pipeline Orchestrator — manage pipeline run state."""
    pass


@cli.command()
@click.option("--input", "input_value", required=True, help="Topic, URL(s), or manifest path")
@click.option("--type", "run_type", required=True,
              type=click.Choice(["topic", "urls", "manifest"]))
@click.option("--options", type=str, help="Pipeline options as JSON string")
def init(input_value, run_type, options):
    """Initialize a new pipeline run."""
    opts = json.loads(options) if options else {}

    # Merge with config defaults
    try:
        cfg = get_pipeline_config()
        defaults = cfg.get("pipeline", {})
        for key in ("max_sources", "max_pages", "auto_approve", "approval_threshold"):
            if key not in opts and key in defaults:
                opts[key] = defaults[key]
    except FileNotFoundError:
        pass

    stats = {
        "sources_discovered": 0,
        "pages_crawled": 0,
        "documents_stored": 0,
        "documents_distilled": 0,
        "approved": 0,
        "refactored": 0,
        "deep_researched": 0,
        "rejected": 0,
        "needs_manual_review": 0,
    }

    # Determine starting stage
    start_stage = "discovery" if run_type == "topic" else "crawling"

    with db_session() as db:
        run_id = db.insert_returning_id(
            """INSERT INTO pipeline_runs
               (run_type, input_value, status, current_stage, options, stats, started_at)
               VALUES (%s, %s, %s, %s, %s, %s, %s)""",
            (run_type, input_value, "running", start_stage,
             json.dumps(opts), json.dumps(stats), datetime.now().isoformat()),
        )

    console.print(f"[green]Pipeline initialized:[/green] run_id={run_id}")
    console.print(f"  Type: {run_type}")
    console.print(f"  Input: {input_value}")
    console.print(f"  Starting stage: {start_stage}")
    click.echo(json.dumps({"run_id": run_id, "status": "running", "stage": start_stage}))


@cli.command()
@click.option("--run-id", required=True, type=int, help="Pipeline run ID")
@click.option("--stage", required=True,
              type=click.Choice(["discovery", "crawling", "storing", "evaluating",
                                 "distilling", "exporting"]))
@click.option("--stats", type=str, help="Stats update as JSON string (merged with existing)")
def advance(run_id, stage, stats):
    """Advance pipeline to the next stage."""
    stats_update = json.loads(stats) if stats else {}

    with db_session() as db:
        run = db.fetch_one(
            "SELECT * FROM pipeline_runs WHERE run_id = %s", (run_id,)
        )
        if not run:
            console.print(f"[red]Error:[/red] run_id={run_id} not found")
            sys.exit(1)

        if run["status"] != "running":
            console.print(f"[red]Error:[/red] Pipeline is {run['status']}, not running")
            sys.exit(1)

        # Merge stats
        current_stats = json.loads(run["stats"]) if isinstance(run["stats"], str) else (run["stats"] or {})
        current_stats.update(stats_update)

        db.execute(
            """UPDATE pipeline_runs
               SET current_stage = %s, stats = %s
               WHERE run_id = %s""",
            (stage, json.dumps(current_stats), run_id),
        )

    stage_num = ["discovery", "crawling", "storing", "evaluating", "distilling", "exporting"].index(stage) + 1
    console.print(f"[green]Pipeline advanced:[/green] Stage {stage_num}/6 — {stage}")
    click.echo(json.dumps({"run_id": run_id, "stage": stage, "stats": current_stats}))


@cli.command()
@click.option("--run-id", required=True, type=int, help="Pipeline run ID")
@click.option("--error", required=True, help="Error message")
@click.option("--stage", required=True, help="Stage where error occurred")
def pause(run_id, error, stage):
    """Pause pipeline due to error."""
    with db_session() as db:
        db.execute(
            """UPDATE pipeline_runs
               SET status = %s, error_message = %s, error_stage = %s
               WHERE run_id = %s""",
            ("paused", error, stage, run_id),
        )

    console.print(f"[yellow]Pipeline paused:[/yellow] run_id={run_id} at {stage}")
    console.print(f"  Error: {error}")
    console.print(f"  Resume with: python pipeline.py resume --run-id {run_id}")


@cli.command()
@click.option("--run-id", required=True, type=int, help="Pipeline run ID to resume")
def resume(run_id):
    """Resume a paused pipeline."""
    with db_session() as db:
        run = db.fetch_one(
            "SELECT * FROM pipeline_runs WHERE run_id = %s", (run_id,)
        )
        if not run:
            console.print(f"[red]Error:[/red] run_id={run_id} not found")
            sys.exit(1)

        if run["status"] != "paused":
            console.print(f"[red]Error:[/red] Pipeline is {run['status']}, not paused")
            sys.exit(1)

        db.execute(
            """UPDATE pipeline_runs
               SET status = %s, error_message = %s
               WHERE run_id = %s""",
            ("running", None, run_id),
        )

    stage = run["current_stage"]
    console.print(f"[green]Pipeline resumed:[/green] run_id={run_id}, continuing from {stage}")
    click.echo(json.dumps({"run_id": run_id, "status": "running", "resume_stage": stage}))


@cli.command()
@click.option("--run-id", required=True, type=int, help="Pipeline run ID")
@click.option("--export-path", type=click.Path(), help="Export output path")
@click.option("--export-count", type=int, help="Number of documents exported")
def complete(run_id, export_path, export_count):
    """Mark pipeline as completed."""
    with db_session() as db:
        db.execute(
            """UPDATE pipeline_runs
               SET status = %s, completed_at = %s, export_path = %s, export_document_count = %s
               WHERE run_id = %s""",
            ("completed", datetime.now().isoformat(), export_path, export_count, run_id),
        )

        run = db.fetch_one("SELECT * FROM pipeline_runs WHERE run_id = %s", (run_id,))

    stats = json.loads(run["stats"]) if isinstance(run["stats"], str) else (run["stats"] or {})

    console.print(f"\n[bold green]Pipeline Complete![/bold green] run_id={run_id}")
    console.print(f"  Sources: {stats.get('sources_discovered', 0)}")
    console.print(f"  Crawled: {stats.get('pages_crawled', 0)}")
    console.print(f"  Stored: {stats.get('documents_stored', 0)}")
    console.print(f"  Approved: {stats.get('approved', 0)}")
    console.print(f"  Rejected: {stats.get('rejected', 0)}")
    if export_path:
        console.print(f"  Exports: {export_path}")

    click.echo(json.dumps({"run_id": run_id, "status": "completed", "stats": stats}, default=str))


@cli.command()
@click.option("--run-id", type=int, help="Show specific run (or latest if omitted)")
@click.option("--all", "show_all", is_flag=True, help="Show all runs")
def status(run_id, show_all):
    """Show pipeline run status."""
    with db_session() as db:
        if run_id:
            rows = db.fetch_all(
                "SELECT * FROM pipeline_runs WHERE run_id = %s", (run_id,)
            )
        elif show_all:
            rows = db.fetch_all(
                "SELECT * FROM pipeline_runs ORDER BY started_at DESC LIMIT %s", (20,)
            )
        else:
            rows = db.fetch_all(
                "SELECT * FROM pipeline_runs ORDER BY started_at DESC LIMIT %s", (1,)
            )

    if not rows:
        console.print("[dim]No pipeline runs found.[/dim]")
        return

    for run in rows:
        stats = json.loads(run["stats"]) if isinstance(run["stats"], str) else (run["stats"] or {})
        status_color = {
            "running": "green", "completed": "blue",
            "failed": "red", "paused": "yellow",
        }.get(run["status"], "white")

        console.print(f"\n[bold]Pipeline Run #{run['run_id']}[/bold]")
        console.print(f"  Status: [{status_color}]{run['status']}[/{status_color}]")
        console.print(f"  Type: {run['run_type']}")
        console.print(f"  Input: {run['input_value']}")
        console.print(f"  Stage: {run['current_stage']}")
        console.print(f"  Started: {run['started_at']}")

        if run.get("error_message"):
            console.print(f"  [red]Error:[/red] {run['error_message']} (at {run.get('error_stage')})")

        if stats:
            table = Table(title="Pipeline Stats", show_header=False)
            table.add_column("Metric")
            table.add_column("Value", justify="right")
            for k, v in stats.items():
                if v:
                    table.add_row(k.replace("_", " ").title(), str(v))
            console.print(table)


@cli.command("track-iteration")
@click.option("--run-id", required=True, type=int, help="Pipeline run ID")
@click.option("--doc-id", required=True, type=int, help="Document ID")
@click.option("--action", required=True, type=click.Choice(["refactor", "deep_research"]),
              help="QA loop action")
def track_iteration(run_id, doc_id, action):
    """Track QA loop iteration for a document.

    Returns the routing decision:
    - 're_distill': proceed with refactor
    - 're_crawl_and_distill': proceed with deep research
    - 'needs_manual_review': max iterations exceeded
    """
    with db_session() as db:
        tracker = db.fetch_one(
            """SELECT * FROM pipeline_iteration_tracker
               WHERE run_id = %s AND doc_id = %s""",
            (run_id, doc_id),
        )

        if tracker:
            refactor_count = tracker.get("refactor_count", 0)
            deep_research_count = tracker.get("deep_research_count", 0)
        else:
            refactor_count = 0
            deep_research_count = 0

        total = refactor_count + deep_research_count

        # Check limits
        if total >= MAX_TOTAL:
            result = "needs_manual_review"
        elif action == "refactor" and refactor_count >= MAX_REFACTOR:
            result = "needs_manual_review"
        elif action == "deep_research" and deep_research_count >= MAX_DEEP_RESEARCH:
            result = "needs_manual_review"
        elif action == "refactor":
            refactor_count += 1
            result = "re_distill"
        else:
            deep_research_count += 1
            result = "re_crawl_and_distill"

        # Upsert tracker
        if tracker:
            db.execute(
                """UPDATE pipeline_iteration_tracker
                   SET refactor_count = %s, deep_research_count = %s,
                       final_decision = %s
                   WHERE run_id = %s AND doc_id = %s""",
                (refactor_count, deep_research_count,
                 "needs_manual_review" if result == "needs_manual_review" else None,
                 run_id, doc_id),
            )
        else:
            db.insert_returning_id(
                """INSERT INTO pipeline_iteration_tracker
                   (run_id, doc_id, refactor_count, deep_research_count, final_decision)
                   VALUES (%s, %s, %s, %s, %s)""",
                (run_id, doc_id, refactor_count, deep_research_count,
                 "needs_manual_review" if result == "needs_manual_review" else None),
            )

    console.print(
        f"[{'yellow' if result == 'needs_manual_review' else 'green'}]"
        f"Iteration tracked:[/] doc_id={doc_id}, {action} "
        f"(refactor: {refactor_count}/{MAX_REFACTOR}, "
        f"research: {deep_research_count}/{MAX_DEEP_RESEARCH}) → {result}"
    )
    click.echo(json.dumps({
        "run_id": run_id,
        "doc_id": doc_id,
        "action": action,
        "result": result,
        "refactor_count": refactor_count,
        "deep_research_count": deep_research_count,
        "total_iterations": refactor_count + deep_research_count,
    }))


if __name__ == "__main__":
    cli()
