"""CLI tests for pulka duplicates."""

from __future__ import annotations

from pathlib import Path

import polars as pl
import pytest

from pulka.cli import main


def _write_parquet(path: Path, data: dict[str, list]) -> Path:
    pl.DataFrame(data).write_parquet(path)
    return path


def test_duplicates_default_all_columns_returns_rows(tmp_path: Path, capsys) -> None:
    dataset = _write_parquet(
        tmp_path / "data.parquet",
        {"id": [1, 1, 2], "val": ["x", "x", "y"]},
    )
    out_path = tmp_path / "duplicates.parquet"

    exit_code = main(["duplicates", str(dataset), "--out", str(out_path)])
    captured = capsys.readouterr()

    assert exit_code == 1
    dupes_df = pl.read_parquet(out_path)
    assert dupes_df.height == 2
    assert dupes_df["id"].to_list() == [1, 1]
    assert dupes_df["val"].to_list() == ["x", "x"]
    assert dupes_df["dupe__count"].to_list() == [2, 2]
    assert "row_id" not in dupes_df.columns
    assert "Duplicates summary" in captured.out
    assert f"Wrote {out_path}" in captured.out


def test_duplicates_cols_drive_detection(tmp_path: Path) -> None:
    dataset = _write_parquet(
        tmp_path / "data.parquet",
        {"id": [1, 2, 3], "status": ["ok", "ok", "bad"], "amount": [1, 2, 3]},
    )
    out_path = tmp_path / "duplicates.parquet"

    exit_code = main(
        [
            "duplicates",
            str(dataset),
            "--cols",
            "status",
            "--out",
            str(out_path),
        ]
    )

    assert exit_code == 1
    dupes_df = pl.read_parquet(out_path)
    assert dupes_df.height == 2
    assert dupes_df["status"].to_list() == ["ok", "ok"]
    assert dupes_df["id"].to_list() == [1, 2]
    assert dupes_df["amount"].to_list() == [1, 2]
    assert dupes_df["dupe__count"].to_list() == [2, 2]


def test_duplicates_groups_mode_returns_one_row_per_group(tmp_path: Path) -> None:
    dataset = _write_parquet(
        tmp_path / "data.parquet",
        {
            "id": [1, 2, 3],
            "status": ["ok", "ok", "bad"],
            "amount": [10, 20, 30],
        },
    )
    out_path = tmp_path / "duplicates_groups.parquet"

    exit_code = main(
        [
            "duplicates",
            str(dataset),
            "--cols",
            "status",
            "--groups",
            "--out",
            str(out_path),
        ]
    )

    assert exit_code == 1
    dupes_df = pl.read_parquet(out_path)
    assert dupes_df.height == 1
    assert dupes_df["status"].to_list() == ["ok"]
    assert dupes_df["dupe__count"].to_list() == [2]


def test_duplicates_keys_are_included_with_cols(tmp_path: Path) -> None:
    dataset = _write_parquet(
        tmp_path / "data.parquet",
        {
            "id": [1, 1, 2, 2],
            "status": ["ok", "ok", "ok", "ok"],
            "amount": [1, 1, 1, 2],
        },
    )
    out_path = tmp_path / "duplicates.parquet"

    exit_code = main(
        [
            "duplicates",
            str(dataset),
            "--keys",
            "id",
            "--cols",
            "status,amount",
            "--out",
            str(out_path),
        ]
    )

    assert exit_code == 1
    dupes_df = pl.read_parquet(out_path)
    assert dupes_df.height == 2
    assert dupes_df["id"].to_list() == [1, 1]
    assert dupes_df["status"].to_list() == ["ok", "ok"]
    assert dupes_df["amount"].to_list() == [1, 1]
    assert dupes_df["dupe__count"].to_list() == [2, 2]


def test_dupes_alias_runs_duplicates(tmp_path: Path) -> None:
    dataset = _write_parquet(tmp_path / "data.parquet", {"a": [1, 1], "b": ["x", "x"]})
    out_path = tmp_path / "duplicates.parquet"

    exit_code = main(["dupes", str(dataset), "--out", str(out_path)])

    assert exit_code == 1
    dupes_df = pl.read_parquet(out_path)
    assert dupes_df.height == 2
    assert dupes_df["dupe__count"].to_list() == [2, 2]


def test_duplicates_tui_launches_viewer(monkeypatch, tmp_path: Path) -> None:
    dataset = _write_parquet(tmp_path / "data.parquet", {"a": [1, 1], "b": ["x", "x"]})

    called: dict[str, object] = {}

    def _run_tui_app(viewer, recorder=None, on_shutdown=None):  # type: ignore[override]
        called["viewer"] = viewer
        called["recorder"] = recorder

    monkeypatch.setattr("pulka.tui.app.run_tui_app", _run_tui_app)
    monkeypatch.setattr("sys.stdin.isatty", lambda: True)
    monkeypatch.setattr("pulka.cli.duplicates._prompt_yes_no", lambda _prompt: True)

    exit_code = main(["duplicates", str(dataset)])

    assert exit_code == 1
    assert called


def test_duplicates_unknown_columns_error(tmp_path: Path, capsys) -> None:
    dataset = _write_parquet(tmp_path / "data.parquet", {"a": [1], "b": [2]})

    with pytest.raises(SystemExit) as exc:
        main(["duplicates", str(dataset), "--cols", "missing"])
    captured = capsys.readouterr()

    assert exc.value.code == 2
    assert "Missing columns" in captured.err


def test_duplicates_warns_on_null_keys(tmp_path: Path, capsys) -> None:
    dataset = _write_parquet(
        tmp_path / "data.parquet",
        {"id": [None, None, 1], "val": ["x", "x", "y"]},
    )
    out_path = tmp_path / "duplicates.parquet"

    exit_code = main(
        [
            "duplicates",
            str(dataset),
            "--keys",
            "id",
            "--cols",
            "val",
            "--out",
            str(out_path),
        ]
    )
    captured = capsys.readouterr()

    assert exit_code == 1
    assert "warning:" in captured.err.lower()
    assert "null" in captured.err.lower()


def test_duplicates_exclude_row_id_allows_key_duplicates(tmp_path: Path) -> None:
    dataset = _write_parquet(
        tmp_path / "data.parquet",
        {"row_id": [1, 2, 3], "id": [1, 1, 2], "val": ["x", "x", "y"]},
    )
    out_path = tmp_path / "duplicates.parquet"

    exit_code = main(
        [
            "duplicates",
            str(dataset),
            "--keys",
            "id",
            "--exclude",
            "row_id",
            "--out",
            str(out_path),
        ]
    )

    assert exit_code == 1
    dupes_df = pl.read_parquet(out_path)
    assert dupes_df.height == 2
    assert dupes_df["id"].to_list() == [1, 1]
    assert dupes_df["val"].to_list() == ["x", "x"]
    assert dupes_df["dupe__count"].to_list() == [2, 2]


def test_duplicates_exclude_unknown_columns_error(tmp_path: Path, capsys) -> None:
    dataset = _write_parquet(tmp_path / "data.parquet", {"a": [1], "b": [2]})

    with pytest.raises(SystemExit) as exc:
        main(["duplicates", str(dataset), "--exclude", "missing"])
    captured = capsys.readouterr()

    assert exc.value.code == 2
    assert "Missing columns" in captured.err
