"""Insight macro handlers extracted from `hiveos.observe`."""

from __future__ import annotations

import json

from hiveos_trace import terminal_style as _term


def _print_list(title, items):
    print(_term.section(title))
    for item in items:
        for wrapped in _term.wrap_text(str(item), initial_indent="  - ", subsequent_indent="    "):
            print(wrapped)


def handle_trace_insight_explain(
    args,
    *,
    maybe_auto_reconcile,
    read_run_record,
    read_trace_events_for_run,
    build_run_summary,
    build_run_diagnosis,
):
    maybe_auto_reconcile()
    run_id = str(args.run_id or "").strip()
    if not run_id:
        raise SystemExit("run_id is required")
    record = read_run_record(run_id)
    if not record:
        raise SystemExit(f"run_id not found: {run_id}")
    limit = max(1, min(20000, int(args.event_limit or 2000)))
    tail_lines = max(1, min(200, int(args.tail_lines or 20)))
    events = read_trace_events_for_run(run_id, limit=limit, trace_log_path=str(record.get("trace_log_path") or ""))
    summary = build_run_summary(record, events, tail_lines=tail_lines)
    diagnosis = build_run_diagnosis(record, events, tail_lines=tail_lines)
    primitive_cmds = [
        f"hive trace summary {run_id} --event-limit {limit} --tail-lines {tail_lines}",
        f"hive trace diagnose {run_id} --event-limit {limit} --tail-lines {tail_lines}",
    ]
    answer = (
        "Run completed successfully."
        if diagnosis.get("status") == "success"
        else f"Run ended in {diagnosis.get('category') or diagnosis.get('status')} state."
    )
    payload = {
        "mode": "macro",
        "macro": "trace.insight.explain",
        "run_id": run_id,
        "answer": answer,
        "confidence": "high" if diagnosis.get("status") == "success" else "medium",
        "evidence": [
            f"status={summary.get('status')}",
            f"duration_ms={summary.get('duration_ms')}",
            f"event_count={summary.get('event_count')}",
            f"runtime_state={summary.get('runtime_state')}",
            f"category={diagnosis.get('category')}",
        ],
        "actions": list(diagnosis.get("recommendations") or []),
        "derived_from": primitive_cmds,
    }
    if args.show_source:
        payload["source"] = {"summary": summary, "diagnosis": diagnosis}
    if args.json:
        print(json.dumps(payload, separators=(",", ":")))
        return 0
    print(_term.section("insight"))
    print(f"  {_term.key('mode')}={payload['mode']} {_term.key('macro')}={payload['macro']}")
    for line in _term.wrap_kv(_term.key("answer"), payload["answer"], indent="  "):
        print(line)
    print(f"  {_term.key('confidence')}={payload['confidence']}")
    _print_list("evidence", payload["evidence"])
    _print_list("actions", payload["actions"])
    _print_list("derived_from", payload["derived_from"])
    if args.emit_primitive:
        print(_term.section("primitive_commands"))
        for cmd in primitive_cmds:
            print(f"  {cmd}")
    if args.show_source:
        print(_term.section("source.summary"))
        print(json.dumps(summary, separators=(",", ":")))
        print(_term.section("source.diagnosis"))
        print(json.dumps(diagnosis, separators=(",", ":")))
    return 0


def handle_trace_insight_drift(
    args,
    *,
    maybe_auto_reconcile,
    read_run_record,
    read_trace_events_for_run,
    build_run_diff,
):
    maybe_auto_reconcile()
    run_id_a = str(args.run_id_a or "").strip()
    run_id_b = str(args.run_id_b or "").strip()
    if not run_id_a or not run_id_b:
        raise SystemExit("run_id_a and run_id_b are required")
    record_a = read_run_record(run_id_a)
    record_b = read_run_record(run_id_b)
    if not record_a:
        raise SystemExit(f"run_id not found: {run_id_a}")
    if not record_b:
        raise SystemExit(f"run_id not found: {run_id_b}")
    limit = max(1, min(20000, int(args.event_limit or 2000)))
    tail_lines = max(1, min(200, int(args.tail_lines or 20)))
    events_a = read_trace_events_for_run(run_id_a, limit=limit, trace_log_path=str(record_a.get("trace_log_path") or ""))
    events_b = read_trace_events_for_run(run_id_b, limit=limit, trace_log_path=str(record_b.get("trace_log_path") or ""))
    diff = build_run_diff(record_a, record_b, events_a, events_b, tail_lines=tail_lines)
    cmd = f"hive trace diff {run_id_a} {run_id_b} --event-limit {limit} --tail-lines {tail_lines}"
    drift_detected = bool(diff.get("config_deltas")) or int((diff.get("execution") or {}).get("first_divergence_index") or -1) >= 0
    if not drift_detected:
        answer = "Runs are materially aligned at inspected granularity."
        confidence = "high"
    elif str((diff.get("run_a") or {}).get("status") or "") != str((diff.get("run_b") or {}).get("status") or ""):
        answer = "Runs drifted to different terminal outcomes."
        confidence = "high"
    else:
        answer = "Runs drifted during execution despite similar terminal status."
        confidence = "medium"
    actions = [
        f"hive trace show {run_id_a} --limit 120",
        f"hive trace show {run_id_b} --limit 120",
    ]
    if int((diff.get("execution") or {}).get("first_divergence_index") or -1) >= 0:
        actions.append("Inspect event stream around first_divergence_index in both runs.")
    if diff.get("config_deltas"):
        actions.append("Review config_deltas and rerun with controlled changes.")
    payload = {
        "mode": "macro",
        "macro": "trace.insight.drift",
        "run_id_a": run_id_a,
        "run_id_b": run_id_b,
        "answer": answer,
        "confidence": confidence,
        "evidence": [
            f"status_a={((diff.get('run_a') or {}).get('status') or '-')}",
            f"status_b={((diff.get('run_b') or {}).get('status') or '-')}",
            f"first_divergence_index={int((diff.get('execution') or {}).get('first_divergence_index') or -1)}",
            f"config_delta_count={len(diff.get('config_deltas') or {})}",
            f"hint={diff.get('hint')}",
        ],
        "actions": actions,
        "derived_from": [cmd],
    }
    if args.show_source:
        payload["source"] = {"diff": diff}
    if args.json:
        print(json.dumps(payload, separators=(",", ":")))
        return 0
    print(_term.section("insight"))
    print(f"  {_term.key('mode')}={payload['mode']} {_term.key('macro')}={payload['macro']}")
    for line in _term.wrap_kv(_term.key("answer"), payload["answer"], indent="  "):
        print(line)
    print(f"  {_term.key('confidence')}={payload['confidence']}")
    _print_list("evidence", payload["evidence"])
    _print_list("actions", payload["actions"])
    _print_list("derived_from", payload["derived_from"])
    if args.emit_primitive:
        print(_term.section("primitive_commands"))
        print(f"  {cmd}")
    if args.show_source:
        print(_term.section("source.diff"))
        print(json.dumps(diff, separators=(",", ":")))
    return 0


def handle_trace_insight_health(
    args,
    *,
    maybe_auto_reconcile,
    parse_duration_window_ms,
    now_ms_fn,
    list_all_run_records,
    record_effective_status,
    record_runtime_state,
    read_trace_events_for_run,
    build_run_diagnosis,
):
    maybe_auto_reconcile()
    window_raw = str(args.window or "24h").strip()
    try:
        window_ms = parse_duration_window_ms(window_raw, default_unit="h")
    except Exception as exc:
        raise SystemExit(f"invalid --window value: {exc}") from exc
    now = now_ms_fn()
    cutoff_ms = max(0, now - window_ms)
    records = [record for record in list_all_run_records() if int(record.get("started_at_ms") or 0) >= cutoff_ms]
    total = len(records)
    status_counts = {"success": 0, "failed": 0, "running": 0, "archived": 0, "other": 0}
    runtime_counts = {"running_active": 0, "running_idle": 0, "running_unknown": 0}
    for record in records:
        status = record_effective_status(record)
        if status in {"success", "failed", "running", "archived"}:
            status_counts[status] += 1
        else:
            status_counts["other"] += 1
        runtime = record_runtime_state(record, now_ms=now)
        if runtime in runtime_counts:
            runtime_counts[runtime] += 1
    success_rate = (status_counts["success"] / max(1, (status_counts["success"] + status_counts["failed"]))) * 100.0
    failure_categories = {}
    sampled_failures = []
    tail_lines = max(1, min(200, int(args.tail_lines or 20)))
    failed_records = [r for r in records if record_effective_status(r) == "failed"][:5]
    failed_command_counts = {}
    failed_command_examples = {}
    for record in records:
        if record_effective_status(record) != "failed":
            continue
        command = str(record.get("command") or "").strip() or "(no command)"
        failed_command_counts[command] = int(failed_command_counts.get(command) or 0) + 1
        failed_command_examples.setdefault(command, str(record.get("run_id") or "").strip())
    for record in failed_records:
        run_id = str(record.get("run_id") or "").strip()
        if not run_id:
            continue
        events = read_trace_events_for_run(run_id, limit=2000, trace_log_path=str(record.get("trace_log_path") or ""))
        diagnosis = build_run_diagnosis(record, events, tail_lines=tail_lines)
        category = str(diagnosis.get("category") or "unknown")
        failure_categories[category] = int(failure_categories.get(category) or 0) + 1
        sampled_failures.append(
            {
                "run_id": run_id,
                "category": category,
                "exit_code": diagnosis.get("exit_code"),
                "duration_ms": diagnosis.get("duration_ms"),
            }
        )
    failed_run_ids = [str(item.get("run_id") or "").strip() for item in sampled_failures if str(item.get("run_id") or "").strip()]
    failure_inspect_commands = [f"hive trace diagnose {run_id} --tail-lines {tail_lines}" for run_id in failed_run_ids]
    top_failed_commands = []
    for command, count in sorted(failed_command_counts.items(), key=lambda item: (-int(item[1]), str(item[0])))[:3]:
        example_run_id = str(failed_command_examples.get(command) or "").strip()
        top_failed_commands.append(
            {
                "command": command,
                "count": int(count),
                "example_run_id": example_run_id or None,
                "inspect_command": f"hive trace diagnose {example_run_id} --tail-lines {tail_lines}" if example_run_id else None,
            }
        )
    cmd = "hive trace ls --status all --limit 200"
    answer = "Run health looks stable in this window." if success_rate >= 80.0 else "Run health shows instability in this window."
    actions = []
    if status_counts["failed"] > 0:
        actions.append("Investigate recent failures with `hive trace diagnose <run_id>` on sampled failed runs.")
    if top_failed_commands:
        actions.append("Prioritize top failing commands first to remove recurring failure classes.")
    if runtime_counts["running_idle"] > 0:
        actions.append("Inspect idle running jobs and reconcile stale ones with `hive trace ops reconcile --stale-after 5m`.")
    if success_rate < 80.0:
        actions.append("Compare recent failures to prior success using `hive trace insight drift <run_a> <run_b>`.")
    if not actions:
        actions.append("Continue monitoring with `hive trace insight health --window 24h`.")
    payload = {
        "mode": "macro",
        "macro": "trace.insight.health",
        "window": window_raw,
        "window_ms": int(window_ms),
        "answer": answer,
        "confidence": "high" if total >= 5 else "medium",
        "summary": {
            "total_runs": total,
            "status_counts": status_counts,
            "runtime_counts": runtime_counts,
            "success_rate_pct": round(success_rate, 2),
        },
        "evidence": [
            f"total_runs={total}",
            f"success={status_counts['success']}",
            f"failed={status_counts['failed']}",
            f"running={status_counts['running']}",
            f"running_idle={runtime_counts['running_idle']}",
            f"success_rate_pct={round(success_rate, 2)}",
        ],
        "actions": actions,
        "failure_category_counts": failure_categories,
        "failed_run_ids": failed_run_ids,
        "failure_inspect_commands": failure_inspect_commands,
        "top_failed_commands": top_failed_commands,
        "sampled_failures": sampled_failures,
        "derived_from": [cmd],
    }
    if args.show_source:
        payload["source"] = {
            "record_run_ids": [str(item.get("run_id") or "") for item in records],
            "sampled_failure_diagnoses": sampled_failures,
        }
    if args.json:
        print(json.dumps(payload, separators=(",", ":")))
        return 0
    print(_term.section("insight"))
    print(
        f"  {_term.key('mode')}={payload['mode']} {_term.key('macro')}={payload['macro']} "
        f"{_term.key('window')}={payload['window']} {_term.key('total_runs')}={payload['summary']['total_runs']} "
        f"{_term.key('success_rate_pct')}={payload['summary']['success_rate_pct']}"
    )
    for line in _term.wrap_kv(_term.key("answer"), payload["answer"], indent="  "):
        print(line)
    print(f"  {_term.key('confidence')}={payload['confidence']}")
    _print_list("evidence", payload["evidence"])
    _print_list("actions", payload["actions"])
    if payload["failure_category_counts"]:
        print(_term.section("failure_category_counts"))
        for key, value in sorted(payload["failure_category_counts"].items()):
            print(f"  {key}: {int(value)}")
    if payload["failed_run_ids"]:
        print(_term.section("failed_run_ids"))
        for run_id in payload["failed_run_ids"]:
            print(f"  - {run_id}")
    if payload["failure_inspect_commands"]:
        print(_term.section("failure_inspect_commands"))
        for inspect_cmd in payload["failure_inspect_commands"]:
            for wrapped in _term.wrap_text(str(inspect_cmd), initial_indent="  ", subsequent_indent="  "):
                print(wrapped)
    if payload["top_failed_commands"]:
        print(_term.section("top_failed_commands"))
        for item in payload["top_failed_commands"]:
            inspect_cmd = str(item.get("inspect_command") or "")
            print(
                f"  count={int(item.get('count') or 0)} "
                f"command={item.get('command') or '-'}"
            )
            if inspect_cmd:
                print(f"    inspect={inspect_cmd}")
    _print_list("derived_from", payload["derived_from"])
    if args.emit_primitive:
        print(_term.section("primitive_commands"))
        print(f"  {cmd}")
    if args.show_source:
        print(_term.section("source"))
        print(json.dumps(payload.get("source") or {}, separators=(",", ":")))
    return 0
