"""Tests for manifest generation."""

import warnings

import pytest

from polytropos.build import manifest


class TestManifestGeneration:
    """Tests for manifest generation from [tool.polytropos.manifest]."""

    def test_basic_manifest(self):
        """Test basic manifest generation."""
        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
            },
            "tool": {
                "polytropos": {
                    "manifest": [
                        {
                            "depends": ["base"],
                        }
                    ]
                }
            },
        }
        result = manifest.generate_manifest(pyproject_data, "19.0")
        assert result == {
            "name": "Test Module",
            "version": "19.0.1.0.0",
            "depends": ["base"],
        }

    def test_default_basic_fields(self):
        """Test that no default fields are added when not declared."""
        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
            },
        }
        result = manifest.generate_manifest(pyproject_data, "19.0")
        assert result == {
            "name": "Test Module",
            "version": "19.0.1.0.0",
        }

    def test_manifest_overrides_defaults(self):
        """Test that manifest config overrides defaults."""
        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
            },
            "tool": {
                "polytropos": {
                    "manifest": [
                        {
                            "category": "Finance",
                            "application": True,
                        }
                    ]
                }
            },
        }
        result = manifest.generate_manifest(pyproject_data, "19.0")
        assert result == {
            "name": "Test Module",
            "version": "19.0.1.0.0",
            "category": "Finance",
            "application": True,
        }


class TestAutoPopulation:
    """Tests for auto-population from [project] in pyproject.toml."""

    def test_auto_author(self):
        """Test that author is auto-populated from pyproject.toml."""
        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
                "authors": [{"name": "Test Author"}],
            },
        }
        result = manifest.generate_manifest(pyproject_data, "19.0")
        assert result == {
            "name": "Test Module",
            "version": "19.0.1.0.0",
            "author": "Test Author",
        }

    def test_auto_license(self):
        """Test that license is auto-populated from pyproject.toml."""
        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
                "license": "MIT",
            },
        }
        result = manifest.generate_manifest(pyproject_data, "19.0")
        assert result == {
            "name": "Test Module",
            "version": "19.0.1.0.0",
            "license": "MIT",
        }

    def test_auto_website(self):
        """Test that website is auto-populated from pyproject.toml."""
        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
                "urls": {"Homepage": "https://example.com"},
            },
        }
        result = manifest.generate_manifest(pyproject_data, "19.0")
        assert result == {
            "name": "Test Module",
            "version": "19.0.1.0.0",
            "website": "https://example.com",
        }

    def test_auto_summary(self):
        """Test that summary is auto-populated from description."""
        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
                "description": "Test description",
            },
        }
        result = manifest.generate_manifest(pyproject_data, "19.0")
        assert result == {
            "name": "Test Module",
            "version": "19.0.1.0.0",
            "summary": "Test description",
        }

    def test_manifest_override_no_warning_when_pyproject_empty(self):
        """Test no warning when pyproject doesn't have the field."""
        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
            },
            "tool": {
                "polytropos": {
                    "manifest": [
                        {
                            "author": "Override Author",
                        }
                    ]
                }
            },
        }
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")
            result = manifest.generate_manifest(pyproject_data, "19.0")
            user_warnings = [x for x in w if issubclass(x.category, UserWarning)]
            assert len(user_warnings) == 0
        assert result == {
            "name": "Test Module",
            "version": "19.0.1.0.0",
            "author": "Override Author",
        }


class TestDeepMerge:
    """Tests for deep merge behavior: dicts merge recursively, lists append, scalars replace."""

    def test_deep_merge_comprehensive(self):
        """Comprehensive test for all merge behaviors in a single config."""
        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
            },
            "tool": {
                "polytropos": {
                    "manifest": [
                        {
                            "depends": ["base"],
                            "application": False,
                            "data": ["data/base.xml"],
                            "assets": {
                                "mod.assets_base": ["mod/static/css/base.css"],
                                "mod.assets_main": [
                                    ["include", "mod.assets_base"],
                                    "mod/static/css/main.css",
                                ],
                            },
                        },
                        {
                            "releases": ">=18",
                            "application": True,
                            "data": ["data/v18.xml"],
                            "assets": {
                                "mod.assets_main": [
                                    ["remove", "mod/static/css/base.css"],
                                    [
                                        "after",
                                        "mod/static/css/main.css",
                                        "mod/static/css/v18.css",
                                    ],
                                ],
                                "mod.assets_v18": ["mod/static/css/v18only.css"],
                            },
                        },
                    ]
                }
            },
        }
        # v17: base manifest only
        result_17 = manifest.generate_manifest(pyproject_data, "17.0")
        assert result_17 == {
            "name": "Test Module",
            "version": "17.0.1.0.0",
            "depends": ["base"],
            "application": False,
            "data": ["data/base.xml"],
            "assets": {
                "mod.assets_base": ["mod/static/css/base.css"],
                "mod.assets_main": [
                    ["include", "mod.assets_base"],
                    "mod/static/css/main.css",
                ],
            },
        }

        # v18: base + override (lists append, scalars replace, dicts merge)
        result_18 = manifest.generate_manifest(pyproject_data, "18.0")
        assert result_18 == {
            "name": "Test Module",
            "version": "18.0.1.0.0",
            "depends": ["base"],
            "application": True,
            "data": ["data/base.xml", "data/v18.xml"],
            "assets": {
                "mod.assets_base": ["mod/static/css/base.css"],
                "mod.assets_main": [
                    ["include", "mod.assets_base"],
                    "mod/static/css/main.css",
                    ["remove", "mod/static/css/base.css"],
                    ["after", "mod/static/css/main.css", "mod/static/css/v18.css"],
                ],
                "mod.assets_v18": ["mod/static/css/v18only.css"],
            },
        }


class TestDependenciesAutoPopulation:
    """Tests for auto-populating depends and external_dependencies from project.dependencies."""

    def test_auto_depends_from_project_dependencies(self):
        """Test that depends is auto-populated from project.dependencies (odoo-addon-* packages)."""
        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
                "dependencies": [
                    "odoo-addon-partner-fax",
                    "odoo-addon-sale",
                ],
            },
        }
        result = manifest.generate_manifest(pyproject_data, "19.0")
        assert result == {
            "name": "Test Module",
            "version": "19.0.1.0.0",
            "depends": ["partner_fax", "sale"],
        }

    def test_auto_external_dependencies_from_project_dependencies(self):
        """Test that external_dependencies.python is auto-populated from project.dependencies (non odoo-addon-*)."""
        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
                "dependencies": [
                    "funcy>=1",
                    "requests>=2.0",
                ],
            },
        }
        result = manifest.generate_manifest(pyproject_data, "19.0")
        assert result == {
            "name": "Test Module",
            "version": "19.0.1.0.0",
            "external_dependencies": {"python": ["funcy>=1", "requests>=2.0"]},
        }

    def test_auto_depends_and_external_dependencies_mixed(self):
        """Test auto-population with both odoo-addon-* and regular dependencies."""
        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
                "dependencies": [
                    "odoo-addon-partner-fax",
                    "funcy>=1",
                    "odoo-addon-sale",
                ],
            },
        }
        result = manifest.generate_manifest(pyproject_data, "19.0")
        assert result == {
            "name": "Test Module",
            "version": "19.0.1.0.0",
            "depends": ["partner_fax", "sale"],
            "external_dependencies": {"python": ["funcy>=1"]},
        }

    def test_auto_depends_with_version_specifiers(self):
        """Test that odoo-addon-* packages with version specifiers are handled correctly."""
        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
                "dependencies": [
                    "odoo-addon-queue-job>=1.0",
                    "odoo-addon-queue-job-cron>=2.0",
                ],
            },
        }
        result = manifest.generate_manifest(pyproject_data, "19.0")
        assert result == {
            "name": "Test Module",
            "version": "19.0.1.0.0",
            "depends": ["queue_job", "queue_job_cron"],
        }

    def test_auto_depends_merged_with_manifest_depends(self):
        """Test that project.dependencies odoo-addon-* packages merge with explicit depends."""
        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
                "dependencies": [
                    "odoo-addon-partner-fax",
                ],
            },
            "tool": {
                "polytropos": {
                    "manifest": [
                        {
                            "depends": ["base", "sale"],
                        }
                    ]
                }
            },
        }
        result = manifest.generate_manifest(pyproject_data, "19.0")
        assert result == {
            "name": "Test Module",
            "version": "19.0.1.0.0",
            "depends": ["partner_fax", "base", "sale"],
        }

    def test_auto_external_dependencies_merged_with_manifest(self):
        """Test that project.dependencies non-odoo-addon packages merge with explicit external_dependencies."""
        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
                "dependencies": [
                    "funcy>=1",
                ],
            },
            "tool": {
                "polytropos": {
                    "manifest": [
                        {
                            "external_dependencies": {
                                "python": ["requests>=2.0"],
                            },
                        }
                    ]
                }
            },
        }
        result = manifest.generate_manifest(pyproject_data, "19.0")
        assert result == {
            "name": "Test Module",
            "version": "19.0.1.0.0",
            "external_dependencies": {"python": ["funcy>=1", "requests>=2.0"]},
        }

    def test_odoo_version_not_in_project_dependencies(self):
        """Test that 'odoo>=X.Y' in project.dependencies never adds odoo to depends.

        The `odoo` dependency is only added to the python package, never to the manifest.
        """
        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
                "dependencies": [
                    "odoo>=18.0",
                ],
            },
        }
        result = manifest.generate_manifest(pyproject_data, "19.0")
        assert result == {
            "name": "Test Module",
            "version": "19.0.1.0.0",
        }

    def test_odoo_version_in_project_dependencies_for_incompatible_release(self):
        """Test that 'odoo>=X.Y' in project.dependencies forbids building for incompatible releases."""
        from polytropos.build.primitives import check_release_compatibility

        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
                "dependencies": [
                    "odoo>=18.0",
                ],
            },
        }
        # Building for 17.0 when odoo>=18.0 is required should error
        with pytest.raises(ValueError, match="Cannot build for Odoo 17.0"):
            check_release_compatibility("17.0", pyproject_data)

    def test_combined_odoo_version_deps_rejected_for_incompatible_release(self):
        """Test that 'odoo>=18.0,<19.0' in a single string rejects Odoo 19."""
        from polytropos.build.primitives import check_release_compatibility

        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
                "dependencies": [
                    "odoo>=18.0,<19.0",
                ],
            },
        }
        with pytest.raises(ValueError, match="Cannot build for Odoo 19.0"):
            check_release_compatibility("19.0", pyproject_data)

    def test_combined_odoo_version_deps_accepted_for_compatible_release(self):
        """Test that 'odoo>=18.0,<19.0' in a single string accepts Odoo 18."""
        from polytropos.build.primitives import check_release_compatibility

        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
                "dependencies": [
                    "odoo>=18.0,<19.0",
                ],
            },
        }
        check_release_compatibility("18.0", pyproject_data)

    def test_split_odoo_version_deps_rejected_for_incompatible_release(self):
        """Test that ['odoo>=18', 'odoo<19'] is rejected when building for Odoo 19."""
        from polytropos.build.primitives import check_release_compatibility

        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
                "dependencies": [
                    "odoo>=18",
                    "odoo<19",
                ],
            },
        }
        with pytest.raises(ValueError, match="Cannot build for Odoo 19.0"):
            check_release_compatibility("19.0", pyproject_data)

    def test_split_odoo_version_deps_rejected_for_too_low(self):
        """Test that ['odoo>=18', 'odoo<19'] is rejected when building for Odoo 17."""
        from polytropos.build.primitives import check_release_compatibility

        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
                "dependencies": [
                    "odoo>=18",
                    "odoo<19",
                ],
            },
        }
        with pytest.raises(ValueError, match="Cannot build for Odoo 17.0"):
            check_release_compatibility("17.0", pyproject_data)

    def test_split_odoo_version_deps_accepted_for_compatible_release(self):
        """Test that ['odoo>=18', 'odoo<19'] is accepted when building for Odoo 18."""
        from polytropos.build.primitives import check_release_compatibility

        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
                "dependencies": [
                    "odoo>=18",
                    "odoo<19",
                ],
            },
        }
        check_release_compatibility("18.0", pyproject_data)

    def test_no_duplicate_depends_when_in_both_sources(self):
        """Test that duplicate dependencies are not added when specified in both sources."""
        pyproject_data = {
            "project": {
                "name": "odoo-addon-test-module",
                "version": "1.0.0",
                "dependencies": [
                    "odoo-addon-sale",
                ],
            },
            "tool": {
                "polytropos": {
                    "manifest": [
                        {
                            "depends": ["sale", "base"],
                        }
                    ]
                }
            },
        }
        result = manifest.generate_manifest(pyproject_data, "19.0")
        assert result == {
            "name": "Test Module",
            "version": "19.0.1.0.0",
            "depends": ["sale", "base"],
        }
