import tomllib
from pathlib import Path
from textwrap import dedent

import pytest

from cibuildwheel.projectfiles import (
    get_requires_python_str,
    resolve_dependency_groups,
    setup_py_python_requires,
)


def test_read_setup_py_simple(tmp_path: Path) -> None:
    with open(tmp_path / "setup.py", "w") as f:
        f.write(
            dedent(
                """
                from setuptools import setup

                setup(
                    name = "hello",
                    other = 23,
                    example = ["item", "other"],
                    python_requires = "1.23",
                )
                """
            )
        )

    assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) == "1.23"
    assert get_requires_python_str(tmp_path, {}) == "1.23"


def test_read_setup_py_if_main(tmp_path: Path) -> None:
    with open(tmp_path / "setup.py", "w") as f:
        f.write(
            dedent(
                """
                from setuptools import setup

                if __name__ == "__main__":
                    setup(
                        name = "hello",
                        other = 23,
                        example = ["item", "other"],
                        python_requires = "1.23",
                    )
                """
            )
        )

    assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) == "1.23"
    assert get_requires_python_str(tmp_path, {}) == "1.23"


def test_read_setup_py_if_main_reversed(tmp_path: Path) -> None:
    with open(tmp_path / "setup.py", "w") as f:
        f.write(
            dedent(
                """
                from setuptools import setup

                if "__main__" == __name__:
                    setup(
                        name = "hello",
                        other = 23,
                        example = ["item", "other"],
                        python_requires = "1.23",
                    )
                """
            )
        )

    assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) == "1.23"
    assert get_requires_python_str(tmp_path, {}) == "1.23"


def test_read_setup_py_if_invalid(tmp_path: Path) -> None:
    with open(tmp_path / "setup.py", "w") as f:
        f.write(
            dedent(
                """
                from setuptools import setup

                if True:
                    setup(
                        name = "hello",
                        other = 23,
                        example = ["item", "other"],
                        python_requires = "1.23",
                    )
                """
            )
        )

    assert not setup_py_python_requires(tmp_path.joinpath("setup.py").read_text())
    assert not get_requires_python_str(tmp_path, {})


def test_read_setup_py_full(tmp_path: Path) -> None:
    with open(tmp_path / "setup.py", "w", encoding="utf8") as f:
        f.write(
            dedent(
                """
                import setuptools

                setuptools.randomfunc()

                setuptools.setup(
                    name = "hello",
                    description = "≥“”ü",
                    other = 23,
                    example = ["item", "other"],
                    python_requires = "1.24",
                )
                """
            )
        )

    assert (
        setup_py_python_requires(tmp_path.joinpath("setup.py").read_text(encoding="utf8")) == "1.24"
    )
    assert get_requires_python_str(tmp_path, {}) == "1.24"


def test_read_setup_py_assign(tmp_path: Path) -> None:
    with open(tmp_path / "setup.py", "w") as f:
        f.write(
            dedent(
                """
                from setuptools import setup

                REQUIRES = "3.21"

                setuptools.setup(
                    name = "hello",
                    other = 23,
                    example = ["item", "other"],
                    python_requires = REQUIRES,
                )
                """
            )
        )

    assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) is None
    assert get_requires_python_str(tmp_path, {}) is None


def test_read_setup_py_None(tmp_path: Path) -> None:
    with open(tmp_path / "setup.py", "w") as f:
        f.write(
            dedent(
                """
                from setuptools import setup

                REQUIRES = None

                setuptools.setup(
                    name = "hello",
                    other = 23,
                    example = ["item", "other"],
                    python_requires = None,
                )
                """
            )
        )

    assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) is None
    assert get_requires_python_str(tmp_path, {}) is None


def test_read_setup_py_empty(tmp_path: Path) -> None:
    with open(tmp_path / "setup.py", "w") as f:
        f.write(
            dedent(
                """
                from setuptools import setup

                REQUIRES = "3.21"

                setuptools.setup(
                    name = "hello",
                    other = 23,
                    example = ["item", "other"],
                )
                """
            )
        )

    assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) is None
    assert get_requires_python_str(tmp_path, {}) is None


def test_read_setup_cfg(tmp_path: Path) -> None:
    with open(tmp_path / "setup.cfg", "w") as f:
        f.write(
            dedent(
                """
                [options]
                python_requires = 1.234
                [metadata]
                something = other
                """
            )
        )

    assert get_requires_python_str(tmp_path, {}) == "1.234"


def test_read_setup_cfg_empty(tmp_path: Path) -> None:
    with open(tmp_path / "setup.cfg", "w") as f:
        f.write(
            dedent(
                """
                [options]
                other = 1.234
                [metadata]
                something = other
                """
            )
        )

    assert get_requires_python_str(tmp_path, {}) is None


def test_read_pyproject_toml(tmp_path: Path) -> None:
    with open(tmp_path / "pyproject.toml", "w") as f:
        f.write(
            dedent(
                """
                [project]
                requires-python = "1.654"

                [tool.cibuildwheel]
                something = "other"
                """
            )
        )
    with open(tmp_path / "pyproject.toml", "rb") as f:
        pyproject_toml = tomllib.load(f)

    assert get_requires_python_str(tmp_path, pyproject_toml) == "1.654"


def test_read_pyproject_toml_empty(tmp_path: Path) -> None:
    with open(tmp_path / "pyproject.toml", "w") as f:
        f.write(
            dedent(
                """
                [project]
                other = 1.234
                """
            )
        )
    with open(tmp_path / "pyproject.toml", "rb") as f:
        pyproject_toml = tomllib.load(f)

    assert get_requires_python_str(tmp_path, pyproject_toml) is None


def test_read_dep_groups() -> None:
    pyproject_toml = {"dependency-groups": {"group1": ["pkg1", "pkg2"], "group2": ["pkg3"]}}
    assert resolve_dependency_groups(pyproject_toml) == ()
    assert resolve_dependency_groups(pyproject_toml, "group1") == ("pkg1", "pkg2")
    assert resolve_dependency_groups(pyproject_toml, "group2") == ("pkg3",)
    assert resolve_dependency_groups(pyproject_toml, "group1", "group2") == ("pkg1", "pkg2", "pkg3")


def test_dep_group_no_file_error() -> None:
    with pytest.raises(FileNotFoundError, match=r"pyproject\.toml"):
        resolve_dependency_groups(None, "test")


def test_dep_group_no_section_error() -> None:
    with pytest.raises(KeyError, match=r"pyproject\.toml"):
        resolve_dependency_groups({}, "test")
