"""proxym compare & proxym batch — multi-dispatch orchestration commands."""

from __future__ import annotations

import sys
import time

import click

from proxym.cli._client import make_client
from proxym.cli._groups.do_ctl import _follow_job, _resolve_all, _run_analyze


# ── Helpers ──────────────────────────────────────────────────


def _dispatch_one(client, ticket_id: str, tool: str, model: str = "") -> str | None:
    """Dispatch a single ticket. Returns job_id or None."""
    payload = {"ticket_id": ticket_id, "tool": tool}
    if model:
        payload["model"] = model
    try:
        r = client.post("/dashboard/tools/dispatch", json=payload)
        if r.status_code >= 400:
            click.echo(f"    Dispatch failed for {tool}: {r.text[:120]}")
            return None
        data = r.json()
        job = data.get("job") or {}
        return job.get("id")
    except Exception as exc:
        click.echo(f"    Dispatch error ({tool}): {exc}")
        return None


def _wait_for_jobs(client, job_ids: list[str], timeout: int = 600, poll: int = 5) -> dict[str, dict]:
    """Poll multiple jobs until all finish or timeout. Returns {job_id: job_dict}."""
    results: dict[str, dict] = {}
    pending = set(job_ids)
    max_polls = max(timeout // poll, 1)

    for tick in range(max_polls):
        if not pending:
            break
        time.sleep(poll)
        for jid in set(pending):
            try:
                r = client.get(f"/dashboard/tools/queue/jobs/{jid}")
                if r.status_code != 200:
                    continue
                job = r.json()
                status = job.get("status", "unknown")
                if status in ("done", "failed", "cancelled"):
                    results[jid] = job
                    pending.discard(jid)
            except Exception:
                pass
        sys.stdout.write(f"\r  Waiting... {len(results)}/{len(job_ids)} done ({tick * poll}s)")
        sys.stdout.flush()

    click.echo()  # newline after progress
    # Mark timed-out jobs
    for jid in pending:
        results[jid] = {"id": jid, "status": "timeout", "duration_s": timeout}
    return results


# ── proxym compare ───────────────────────────────────────────


@click.command("compare")
@click.argument("task")
@click.option("--on", "project_name", default=None, help="Project name or id")
@click.option("--tools", "tools_csv", required=True, help="Comma-separated tool names (e.g. aider,claude-code)")
@click.option("--model", "model_name", default=None, help="Model alias (same for all tools)")
@click.option("--timeout", type=int, default=600, help="Wait timeout in seconds")
@click.option("--type", "task_type", default=None, help="Task type")
@click.pass_context
def compare_cmd(ctx, task, project_name, tools_csv, model_name, timeout, task_type):
    """A/B test: send same task to multiple tools and compare results.

    \b
    Examples:
      proxym compare "add cache to API" --on my-project --tools aider,claude-code
      proxym compare "add tests" --on my-project --tools aider,claude-code,roo-code
    """
    obj = ctx.obj or {}
    client = make_client(obj.get("proxy"), obj.get("key"))
    tool_names = [t.strip() for t in tools_csv.split(",") if t.strip()]

    if len(tool_names) < 2:
        click.echo("  Error: --tools must specify at least 2 tools (comma-separated).")
        ctx.exit(1)
        return

    # Resolve project/type/priority (use first tool as default, overridden per dispatch)
    project, task_type, _, priority, model, _pcfg = _resolve_all(
        client, task, project_name, None, task_type, None, model_name,
    )
    if project_name and not project:
        click.echo(f"  Project '{project_name}' not found.")
        ctx.exit(1)
        return

    click.echo(f"\n  Compare: \"{task[:60]}\"")
    click.echo(f"  Tools:   {', '.join(tool_names)}")
    if project:
        click.echo(f"  Project: {project['name']}")
    click.echo()

    # Create one ticket per tool and dispatch
    dispatched: list[dict] = []  # [{tool, ticket_id, job_id}]
    for tool_name in tool_names:
        payload = {
            "task": task,
            "type": task_type,
            "priority": priority,
            "tool": tool_name,
        }
        if project:
            payload["project_id"] = project.get("id", "")
        try:
            r = client.post("/dashboard/tickets", json=payload)
            r.raise_for_status()
            ticket_id = r.json().get("id", "?")
        except Exception as exc:
            click.echo(f"    {tool_name}: failed to create ticket: {exc}")
            continue

        job_id = _dispatch_one(client, ticket_id, tool_name, model)
        if job_id:
            dispatched.append({"tool": tool_name, "ticket_id": ticket_id, "job_id": job_id})
            click.echo(f"    {tool_name}: {ticket_id} → job {job_id}")
        else:
            dispatched.append({"tool": tool_name, "ticket_id": ticket_id, "job_id": None})
            click.echo(f"    {tool_name}: {ticket_id} → dispatch failed")

    # Wait for all jobs
    active_jobs = [d["job_id"] for d in dispatched if d["job_id"]]
    if not active_jobs:
        click.echo("\n  No jobs dispatched. Aborting.")
        ctx.exit(1)
        return

    click.echo(f"\n  Waiting for {len(active_jobs)} jobs (timeout={timeout}s)...")
    job_results = _wait_for_jobs(client, active_jobs, timeout=timeout)

    # Build comparison table
    click.echo(f"\n  {'Tool':<16s} {'Status':<10s} {'Time':>7s} {'Files':>6s} {'Lines':>7s}")
    click.echo(f"  {'─' * 16} {'─' * 10} {'─' * 7} {'─' * 6} {'─' * 7}")

    for d in dispatched:
        tool_name = d["tool"]
        jid = d.get("job_id")
        if not jid:
            click.echo(f"  {tool_name:<16s} \033[31m✗ skip\033[0m ")
            continue

        job = job_results.get(jid, {})
        status = job.get("status", "?")
        dur = job.get("duration_s", 0)
        result = job.get("result") or {}
        files = result.get("files_changed", [])
        # Approximate lines from stdout
        stdout = result.get("stdout", "")
        lines_added = stdout.count("\n+") if stdout else 0

        if status == "done":
            st_str = "\033[32m✓ PASS\033[0m "
        elif status == "failed":
            st_str = "\033[31m✗ FAIL\033[0m "
        elif status == "timeout":
            st_str = "\033[33m⏱ TIME\033[0m "
        else:
            st_str = f"  {status:<6s}"

        click.echo(
            f"  {tool_name:<16s} {st_str} {dur:6.0f}s {len(files):>6d} {lines_added:>7d}"
        )

    # Show follow-up hints
    click.echo()
    for d in dispatched:
        if d.get("job_id"):
            click.echo(f"  proxym tools job {d['job_id']}   # {d['tool']} output")
    click.echo()


# ── proxym batch ─────────────────────────────────────────────


@click.command("batch")
@click.argument("yaml_file", type=click.Path(exists=True))
@click.option("--wait", is_flag=True, help="Wait for all jobs to complete")
@click.option("--analyze", is_flag=True, help="Run analysis after completion")
@click.option("--parallel", is_flag=True, help="Dispatch all jobs at once (default: sequential)")
@click.option("--timeout", type=int, default=600, help="Wait timeout per job in seconds")
@click.pass_context
def batch_cmd(ctx, yaml_file, wait, analyze, parallel, timeout):
    """Execute multiple tasks from a YAML file.

    \b
    YAML format:
      - task: "Create calculator.py"
        project: calculator
        tool: aider
        type: feature
        priority: high
      - task: "Write tests"
        project: calculator
        tool: claude-code
        type: test

    \b
    Examples:
      proxym batch tickets.yaml --wait --analyze
      proxym batch tickets.yaml --parallel --wait
    """
    import yaml

    obj = ctx.obj or {}
    client = make_client(obj.get("proxy"), obj.get("key"))

    try:
        with open(yaml_file, "r", encoding="utf-8") as f:
            items = yaml.safe_load(f)
    except Exception as exc:
        click.echo(f"  Error reading {yaml_file}: {exc}")
        ctx.exit(1)
        return

    if not isinstance(items, list):
        click.echo(f"  Error: {yaml_file} must contain a YAML list of tasks.")
        ctx.exit(1)
        return

    click.echo(f"\n  Batch: {len(items)} tasks from {yaml_file}")
    click.echo(f"  Mode: {'parallel' if parallel else 'sequential'}")
    click.echo()

    # Resolve + create + dispatch all items
    dispatched: list[dict] = []
    for i, item in enumerate(items, 1):
        task = item.get("task", "")
        if not task:
            click.echo(f"  [{i}] Skipped — no task description.")
            continue

        proj_name = item.get("project")
        tool_name = item.get("tool")
        task_type = item.get("type")
        priority = item.get("priority")
        model_name = item.get("model")

        project, resolved_type, tool, resolved_prio, model, _pcfg = _resolve_all(
            client, task, proj_name, tool_name, task_type, priority, model_name,
        )

        # Create ticket
        payload = {
            "task": task,
            "type": resolved_type,
            "priority": resolved_prio,
            "tool": tool,
        }
        if project:
            payload["project_id"] = project.get("id", "")

        try:
            r = client.post("/dashboard/tickets", json=payload)
            r.raise_for_status()
            ticket_id = r.json().get("id", "?")
        except Exception as exc:
            click.echo(f"  [{i}] Failed to create ticket: {exc}")
            continue

        # Dispatch
        job_id = _dispatch_one(client, ticket_id, tool, model)
        entry = {"index": i, "task": task[:50], "tool": tool, "ticket_id": ticket_id, "job_id": job_id}
        dispatched.append(entry)

        status = f"→ job {job_id}" if job_id else "→ dispatch failed"
        click.echo(f"  [{i}] {ticket_id}  {tool:<14s}  {status}")

        # Sequential mode: wait for each job before next dispatch
        if not parallel and wait and job_id:
            _follow_job(client, job_id, timeout=timeout)
            if analyze:
                _run_analyze(client, ticket_id)

    # Parallel mode: wait for all at once
    if parallel and wait:
        active_jobs = [d["job_id"] for d in dispatched if d["job_id"]]
        if active_jobs:
            click.echo(f"\n  Waiting for {len(active_jobs)} jobs...")
            _wait_for_jobs(client, active_jobs, timeout=timeout)

            if analyze:
                for d in dispatched:
                    if d.get("job_id"):
                        _run_analyze(client, d["ticket_id"])

    # Summary
    total = len(dispatched)
    ok = sum(1 for d in dispatched if d.get("job_id"))
    click.echo(f"\n  Batch complete: {ok}/{total} dispatched")
    if not wait:
        click.echo("  Use --wait to follow job completion.")
    click.echo()
