"""Tests para drain_fleet_events."""

import json

from drain_fleet_events import _normalize_fn_run_flags, drain_fleet_events


def _write_queue(path, events):
    """Escribe una lista de eventos como JSONL en path."""
    with open(path, "w", encoding="utf-8") as fh:
        for ev in events:
            fh.write(json.dumps(ev) + "\n")


def _make_event(to, urgent=False, session_id="uuid", from_state="TRABAJANDO"):
    return {
        "ts": 1750445000000,
        "session_id": session_id,
        "pid": 1234,
        "from": from_state,
        "to": to,
        "goal": "objetivo de prueba",
        "phase": "preguntando",
        "urgent": urgent,
    }


def test_cola_con_eventos_cursor_cero_devuelve_todos_y_avanza(tmp_path):
    events_path = tmp_path / "events.jsonl"
    cursor_path = tmp_path / "cursor"
    eventos = [
        _make_event("RECLAMA", urgent=True),
        _make_event("DICE_TERMINADO"),
        _make_event("ESTANCADO"),
    ]
    _write_queue(events_path, eventos)

    result = drain_fleet_events(str(events_path), str(cursor_path))

    assert result["total_new"] == 3
    assert len(result["events"]) == 3
    assert result["cursor"] == 3
    assert "reset" not in result
    # El cursor se persistio en disco.
    assert cursor_path.read_text().strip() == "3"


def test_segunda_llamada_cero_nuevos(tmp_path):
    events_path = tmp_path / "events.jsonl"
    cursor_path = tmp_path / "cursor"
    _write_queue(events_path, [_make_event("RECLAMA"), _make_event("ESTANCADO")])

    first = drain_fleet_events(str(events_path), str(cursor_path))
    assert first["total_new"] == 2

    second = drain_fleet_events(str(events_path), str(cursor_path))
    assert second["total_new"] == 0
    assert second["events"] == []
    assert second["by_classification"] == {}
    assert second["urgent"] == []
    assert second["cursor"] == 2


def test_advance_false_no_mueve_cursor(tmp_path):
    events_path = tmp_path / "events.jsonl"
    cursor_path = tmp_path / "cursor"
    _write_queue(events_path, [_make_event("RECLAMA"), _make_event("GONE")])

    peek1 = drain_fleet_events(str(events_path), str(cursor_path), advance=False)
    assert peek1["total_new"] == 2
    # No se creo archivo de cursor.
    assert not cursor_path.exists()

    # Una segunda lectura con peek vuelve a ver los mismos eventos.
    peek2 = drain_fleet_events(str(events_path), str(cursor_path), advance=False)
    assert peek2["total_new"] == 2
    assert not cursor_path.exists()


def test_archivo_ausente_vacio_sin_crash(tmp_path):
    events_path = tmp_path / "no_existe.jsonl"
    cursor_path = tmp_path / "cursor"

    result = drain_fleet_events(str(events_path), str(cursor_path))

    assert result["total_new"] == 0
    assert result["events"] == []
    assert result["by_classification"] == {}
    assert result["urgent"] == []
    assert result["cursor"] == 0


def test_json_invalido_se_salta(tmp_path):
    events_path = tmp_path / "events.jsonl"
    cursor_path = tmp_path / "cursor"
    with open(events_path, "w", encoding="utf-8") as fh:
        fh.write(json.dumps(_make_event("RECLAMA")) + "\n")
        fh.write("esto no es json valido {{{\n")
        fh.write("\n")  # linea en blanco
        fh.write("   \n")  # linea solo espacios
        fh.write(json.dumps(_make_event("ESTANCADO")) + "\n")

    result = drain_fleet_events(str(events_path), str(cursor_path))

    # Solo 2 eventos validos, pero el cursor avanza al total de lineas (5).
    assert result["total_new"] == 2
    assert result["cursor"] == 5
    assert [e["to"] for e in result["events"]] == ["RECLAMA", "ESTANCADO"]


def test_agrupacion_por_to_y_urgent(tmp_path):
    events_path = tmp_path / "events.jsonl"
    cursor_path = tmp_path / "cursor"
    eventos = [
        _make_event("RECLAMA", urgent=True, session_id="a"),
        _make_event("RECLAMA", urgent=False, session_id="b"),
        _make_event("DICE_TERMINADO", urgent=False, session_id="c"),
        _make_event("ESTANCADO", urgent=True, session_id="d"),
    ]
    _write_queue(events_path, eventos)

    result = drain_fleet_events(str(events_path), str(cursor_path))

    bc = result["by_classification"]
    assert set(bc.keys()) == {"RECLAMA", "DICE_TERMINADO", "ESTANCADO"}
    assert len(bc["RECLAMA"]) == 2
    assert len(bc["DICE_TERMINADO"]) == 1
    assert len(bc["ESTANCADO"]) == 1

    assert len(result["urgent"]) == 2
    urgent_sessions = {e["session_id"] for e in result["urgent"]}
    assert urgent_sessions == {"a", "d"}


def test_normaliza_flag_advance_false_posicional():
    # `fn run drain_fleet_events --advance false` aplana a estos posicionales.
    ev, cur, adv = _normalize_fn_run_flags("--advance", "false", True)
    assert (ev, cur, adv) == (None, None, False)


def test_normaliza_flag_advance_true_posicional():
    ev, cur, adv = _normalize_fn_run_flags("--advance", "true", True)
    assert (ev, cur, adv) == (None, None, True)


def test_normaliza_flag_advance_presencia_sin_valor():
    # `--advance` solo (sin valor): presencia => True.
    ev, cur, adv = _normalize_fn_run_flags("--advance", None, True)
    assert (ev, cur, adv) == (None, None, True)


def test_normaliza_flag_advance_cero_es_false():
    ev, cur, adv = _normalize_fn_run_flags("--advance", "0", True)
    assert adv is False


def test_no_toca_llamada_normal_por_kwargs():
    # Una llamada normal con rutas reales no se altera.
    ev, cur, adv = _normalize_fn_run_flags("/tmp/q.jsonl", "/tmp/cursor", False)
    assert (ev, cur, adv) == ("/tmp/q.jsonl", "/tmp/cursor", False)
    ev2, cur2, adv2 = _normalize_fn_run_flags(None, None, True)
    assert (ev2, cur2, adv2) == (None, None, True)


def test_peek_flag_posicional_no_mueve_cursor(tmp_path):
    # End-to-end del patron `--advance false` sobre una cola controlada: como el
    # normalizador descarta las rutas, apuntamos via env HOME a tmp_path.
    fleet_dir = tmp_path / ".claude" / "fleet"
    fleet_dir.mkdir(parents=True)
    _write_queue(fleet_dir / "events.jsonl", [_make_event("RECLAMA")])
    import os as _os

    prev_home = _os.environ.get("HOME")
    _os.environ["HOME"] = str(tmp_path)
    try:
        # Simula exactamente lo que pasa el runner de `fn run`.
        result = drain_fleet_events("--advance", "false")
    finally:
        if prev_home is not None:
            _os.environ["HOME"] = prev_home
    assert result["total_new"] == 1
    # advance=False => no se persiste cursor.
    assert not (fleet_dir / "cursor").exists()


def test_cursor_mayor_que_lineas_reinicia(tmp_path):
    events_path = tmp_path / "events.jsonl"
    cursor_path = tmp_path / "cursor"
    # Cola con 2 lineas pero cursor apuntando a 99 (cola rotada/truncada).
    _write_queue(events_path, [_make_event("RECLAMA"), _make_event("GONE")])
    cursor_path.write_text("99")

    result = drain_fleet_events(str(events_path), str(cursor_path))

    assert result.get("reset") is True
    assert result["total_new"] == 2
    assert result["cursor"] == 2
    assert cursor_path.read_text().strip() == "2"
