from __future__ import annotations

from argparse import Action, ArgumentParser, Namespace
from typing import TYPE_CHECKING, TypedDict
from unittest.mock import MagicMock

import pytest

from tox.report import HandledError
from tox.session.env_select import _env_completer, register_env_select_flags  # ruff:ignore[import-private-name]

if TYPE_CHECKING:
    from pathlib import Path

    from pytest_mock import MockerFixture


class _CompleterArgs(TypedDict):
    prefix: str
    action: Action
    parser: ArgumentParser
    parsed_args: Namespace


@pytest.fixture
def completer_args() -> _CompleterArgs:
    return {
        "prefix": "",
        "action": MagicMock(spec=Action),
        "parser": MagicMock(spec=ArgumentParser),
        "parsed_args": MagicMock(spec=Namespace),
    }


def test_env_completer_ini(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, completer_args: _CompleterArgs) -> None:
    config = tmp_path / "tox.ini"
    config.write_text("[tox]\nenv_list = py311,lint\n[testenv:docs]\n")
    monkeypatch.chdir(tmp_path)
    result = _env_completer(**completer_args)
    assert "ALL" in result
    assert "py311" in result
    assert "lint" in result
    assert "docs" in result


def test_env_completer_toml(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, completer_args: _CompleterArgs) -> None:
    config = tmp_path / "tox.toml"
    config.write_text('[env.lint]\ncommands = [["ruff", "check"]]\n')
    monkeypatch.chdir(tmp_path)
    result = _env_completer(**completer_args)
    assert "ALL" in result
    assert "lint" in result


def test_env_completer_no_config(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch, completer_args: _CompleterArgs
) -> None:
    monkeypatch.chdir(tmp_path)
    result = _env_completer(**completer_args)
    assert result == ["ALL"]


def test_env_completer_handled_error(mocker: MockerFixture, completer_args: _CompleterArgs) -> None:
    mocker.patch("tox.session.env_select.discover_source", side_effect=HandledError("bad config"))
    result = _env_completer(**completer_args)
    assert result == []


def test_env_completer_includes_plugin_envs(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch, mocker: MockerFixture, completer_args: _CompleterArgs
) -> None:
    config = tmp_path / "tox.ini"
    config.write_text("[tox]\nenv_list = py311\n")
    monkeypatch.chdir(tmp_path)
    mocker.patch("tox.plugin.manager.Plugin.tox_extend_envs", return_value=[["plugin-env"]])
    result = _env_completer(**completer_args)
    assert "ALL" in result
    assert "py311" in result
    assert "plugin-env" in result


def test_register_env_select_attaches_completer() -> None:
    parser = ArgumentParser()
    register_env_select_flags(parser, default=None, multiple=False)
    action = next(a for a in parser._actions if a.dest == "env")  # ruff:ignore[private-member-access]
    assert hasattr(action, "completer")
    assert action.completer is _env_completer
