"""Integration tests for Polytropos editable installs."""

import ast
import textwrap
from pathlib import Path
from zipfile import ZipFile

import plumbum
import pytest

from polytropos.build import build_editable
from tests.conftest import build_file_tree


def extract_manifest_from_file(module_path: Path) -> dict:
    """Extract manifest dict from module directory."""
    manifest_path = module_path / "__manifest__.py"
    content = manifest_path.read_text()
    content = content.replace("true", "True").replace("false", "False")
    manifest_str = content.split("{", 1)[1].rsplit("}", 1)[0]
    return ast.literal_eval("{" + manifest_str + "}")


class TestEditableBuild:
    """Integration tests for editable installs of Odoo modules."""

    def test_basic_editable(self, tmp_path):
        """Test editable install of a basic module."""
        with plumbum.local.cwd(tmp_path):
            build_file_tree(
                {
                    "pyproject.toml": textwrap.dedent("""\
                        [build-system]
                        requires = ["polytropos[build]"]
                        build-backend = "polytropos.build"

                        [project]
                        name = "odoo-addon-test-module"
                        version = "1.0.0"
                        requires-python = ">=3.11"
                        dynamic = ["version"]

                        [tool.polytropos]
                        default_odoo_release = "17.0"

                        [[tool.polytropos.manifest]]
                        depends = []
                    """),
                    "__init__.py": "",
                },
                base=tmp_path,
            )

            editable_dir = tmp_path / "editable"
            editable_dir.mkdir()

            result = build_editable(str(editable_dir), {"odoo_release": "17.0"})

            assert result == "odoo_addon_test_module-17.0.1.0.0-py3-none-any.whl"

            manifest = extract_manifest_from_file(tmp_path)
            assert manifest == {
                "name": "Test Module",
                "version": "17.0.1.0.0",
                "depends": [],
            }

            wheel_path = editable_dir / result
            assert wheel_path.exists()

            with ZipFile(wheel_path, "r") as whl:
                names = whl.namelist()
                assert "odoo_addon_test_module.pth" in names

    def test_editable_pth_points_to_real_path(self, tmp_path):
        """Test that .pth file in editable wheel points to an existing path."""
        with plumbum.local.cwd(tmp_path):
            build_file_tree(
                {
                    "pyproject.toml": textwrap.dedent("""\
                        [build-system]
                        requires = ["polytropos[build]"]
                        build-backend = "polytropos.build"

                        [project]
                        name = "odoo-addon-test-module"
                        version = "1.0.0"
                        requires-python = ">=3.11"
                        dynamic = ["version"]

                        [tool.polytropos]
                        default_odoo_release = "17.0"

                        [[tool.polytropos.manifest]]
                        depends = []
                    """),
                    "__init__.py": "",
                },
                base=tmp_path,
            )

            editable_dir = tmp_path / "editable"
            editable_dir.mkdir()

            result = build_editable(str(editable_dir), {"odoo_release": "17.0"})

            wheel_path = editable_dir / result
            with ZipFile(wheel_path, "r") as whl:
                pth_content = whl.read("odoo_addon_test_module.pth").decode().strip()
                pth_path = Path(pth_content)
                assert pth_path == tmp_path / "build" / "__editable__", (
                    f"Editable .pth path incorrect: {pth_content}"
                )

    def test_editable_with_depends(self, tmp_path):
        """Test editable install with dependencies."""
        with plumbum.local.cwd(tmp_path):
            build_file_tree(
                {
                    "pyproject.toml": textwrap.dedent("""\
                        [build-system]
                        requires = ["polytropos[build]"]
                        build-backend = "polytropos.build"

                        [project]
                        name = "odoo-addon-test-module"
                        version = "1.0.0"
                        requires-python = ">=3.11"
                        dynamic = ["version"]

                        [tool.polytropos]
                        default_odoo_release = "17.0"

                        [[tool.polytropos.manifest]]
                        depends = ["base", "web"]
                    """),
                    "__init__.py": "",
                },
                base=tmp_path,
            )

            editable_dir = tmp_path / "editable"
            editable_dir.mkdir()

            result = build_editable(str(editable_dir), {"odoo_release": "17.0"})

            assert result == "odoo_addon_test_module-17.0.1.0.0-py3-none-any.whl"

            manifest = extract_manifest_from_file(tmp_path)
            assert manifest == {
                "name": "Test Module",
                "version": "17.0.1.0.0",
                "depends": ["base", "web"],
            }

    def test_editable_with_data_files(self, tmp_path):
        """Test editable install with data files."""
        with plumbum.local.cwd(tmp_path):
            build_file_tree(
                {
                    "pyproject.toml": textwrap.dedent("""\
                        [build-system]
                        requires = ["polytropos[build]"]
                        build-backend = "polytropos.build"

                        [project]
                        name = "odoo-addon-test-module"
                        version = "1.0.0"
                        requires-python = ">=3.11"
                        dynamic = ["version"]

                        [tool.polytropos]
                        default_odoo_release = "17.0"

                        [[tool.polytropos.manifest]]
                        depends = ["base"]
                        data = ["data/views.xml"]
                    """),
                    "__init__.py": "",
                    "data/views.xml": '<?xml version="1.0"?><odoo/>',
                },
                base=tmp_path,
            )

            editable_dir = tmp_path / "editable"
            editable_dir.mkdir()

            result = build_editable(str(editable_dir), {"odoo_release": "17.0"})

            assert result == "odoo_addon_test_module-17.0.1.0.0-py3-none-any.whl"

            manifest = extract_manifest_from_file(tmp_path)
            assert manifest == {
                "name": "Test Module",
                "version": "17.0.1.0.0",
                "depends": ["base"],
                "data": ["data/views.xml"],
            }

    def test_editable_error_no_release(self, tmp_path):
        """Test that editable build fails without Odoo release."""
        with plumbum.local.cwd(tmp_path):
            build_file_tree(
                {
                    "pyproject.toml": textwrap.dedent("""\
                        [build-system]
                        requires = ["polytropos[build]"]
                        build-backend = "polytropos.build"

                        [project]
                        name = "odoo-addon-test-module"
                        version = "1.0.0"
                        requires-python = ">=3.11"
                        dynamic = ["version"]
                    """),
                    "__init__.py": "",
                },
                base=tmp_path,
            )

            editable_dir = tmp_path / "editable"
            editable_dir.mkdir()

            with pytest.raises(ValueError, match="Odoo release not specified"):
                build_editable(str(editable_dir), None)

    def test_editable_missing_data_file(self, tmp_path):
        """Test that editable build fails if data file is missing."""
        with plumbum.local.cwd(tmp_path):
            build_file_tree(
                {
                    "pyproject.toml": textwrap.dedent("""\
                        [build-system]
                        requires = ["polytropos[build]"]
                        build-backend = "polytropos.build"

                        [project]
                        name = "odoo-addon-test-module"
                        version = "1.0.0"
                        requires-python = ">=3.11"
                        dynamic = ["version"]

                        [tool.polytropos]
                        default_odoo_release = "17.0"

                        [[tool.polytropos.manifest]]
                        depends = ["base"]
                        data = ["data/missing.xml"]
                    """),
                    "__init__.py": "",
                },
                base=tmp_path,
            )

            editable_dir = tmp_path / "editable"
            editable_dir.mkdir()

            with pytest.raises(FileNotFoundError, match="Data file not found"):
                build_editable(str(editable_dir), {"odoo_release": "17.0"})

    def test_editable_missing_data_file_no_artifacts(self, tmp_path):
        """Test that missing data file doesn't leave artifacts behind."""
        with plumbum.local.cwd(tmp_path):
            build_file_tree(
                {
                    "pyproject.toml": textwrap.dedent("""\
                        [build-system]
                        requires = ["polytropos[build]"]
                        build-backend = "polytropos.build"

                        [project]
                        name = "odoo-addon-test-module"
                        version = "1.0.0"
                        requires-python = ">=3.11"
                        dynamic = ["version"]

                        [tool.polytropos]
                        default_odoo_release = "17.0"

                        [[tool.polytropos.manifest]]
                        depends = ["base"]
                        data = ["data/missing.xml"]
                    """),
                    "__init__.py": "",
                },
                base=tmp_path,
            )

            editable_dir = tmp_path / "editable"
            editable_dir.mkdir()

            with pytest.raises(FileNotFoundError):
                build_editable(str(editable_dir), {"odoo_release": "17.0"})

            pth_file = editable_dir / "test_module.pth"
            assert not pth_file.exists()

    def test_editable_src_layout_with_data_files(self, tmp_path):
        """Test editable install with src layout and data files."""
        with plumbum.local.cwd(tmp_path):
            build_file_tree(
                {
                    "pyproject.toml": textwrap.dedent("""\
                        [build-system]
                        requires = ["polytropos[build]"]
                        build-backend = "polytropos.build"

                        [project]
                        name = "odoo-addon-test-module"
                        version = "1.0.0"
                        requires-python = ">=3.11"
                        dynamic = ["version"]

                        [tool.polytropos]
                        default_odoo_release = "18.0"
                        src = "src"

                        [[tool.polytropos.manifest]]
                        depends = ["base"]
                        data = ["data/views.xml"]
                    """),
                    "src/__init__.py": "",
                    "src/data/views.xml": '<?xml version="1.0"?><odoo/>',
                },
                base=tmp_path,
            )

            editable_dir = tmp_path / "editable"
            editable_dir.mkdir()

            result = build_editable(str(editable_dir), {"odoo_release": "18.0"})

            assert result == "odoo_addon_test_module-18.0.1.0.0-py3-none-any.whl"

            manifest = extract_manifest_from_file(tmp_path / "src")
            assert manifest == {
                "name": "Test Module",
                "version": "18.0.1.0.0",
                "depends": ["base"],
                "data": ["data/views.xml"],
            }

    def test_editable_src_layout_missing_data_file(self, tmp_path):
        """Test that editable build fails if data file is missing in src layout."""
        with plumbum.local.cwd(tmp_path):
            build_file_tree(
                {
                    "pyproject.toml": textwrap.dedent("""\
                        [build-system]
                        requires = ["polytropos[build]"]
                        build-backend = "polytropos.build"

                        [project]
                        name = "odoo-addon-test-module"
                        version = "1.0.0"
                        requires-python = ">=3.11"
                        dynamic = ["version"]

                        [tool.polytropos]
                        default_odoo_release = "17.0"
                        src = "src"

                        [[tool.polytropos.manifest]]
                        depends = ["base"]
                        data = ["data/missing.xml"]
                    """),
                    "src/__init__.py": "",
                },
                base=tmp_path,
            )

            editable_dir = tmp_path / "editable"
            editable_dir.mkdir()

            with pytest.raises(FileNotFoundError, match="Data file not found"):
                build_editable(str(editable_dir), {"odoo_release": "17.0"})
