# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.

import tempfile
import unittest
from pathlib import Path

from scripts.release_licenses import (
    LicenseCorrection,
    PYTHON_MIT_CORRECTIONS,
    binary_notice,
    correction_html,
    resolved_packages,
)


class LicenseCorrectionsTest(unittest.TestCase):
    def test_python_corrections_cover_jieba_workspace(self) -> None:
        corrections = [
            correction
            for correction in PYTHON_MIT_CORRECTIONS
            if correction.crates == ("jieba-macros", "jieba-rs")
        ]
        self.assertEqual(len(corrections), 1)
        correction = corrections[0]
        self.assertEqual(correction.license_crate, "jieba-rs")
        self.assertEqual(
            correction.license_path,
            "third-party-licenses/jieba-rs-0.10.3.LICENSE",
        )
        root = Path(__file__).resolve().parents[2]
        self.assertTrue((root / correction.license_path).is_file())

    def test_repository_license_correction_reads_checked_in_file(self) -> None:
        with tempfile.TemporaryDirectory() as temp:
            root = Path(temp)
            license_path = root / "third-party-licenses" / "jieba.LICENSE"
            license_path.parent.mkdir()
            license_path.write_text("Jieba workspace license\n", encoding="utf-8")
            metadata = {
                "normal_packages": {
                    ("jieba-macros", "0.10.3"),
                    ("jieba-rs", "0.10.3"),
                },
                "packages": [
                    {
                        "name": name,
                        "version": "0.10.3",
                        "manifest_path": str(root / name / "Cargo.toml"),
                        "repository": "https://example.com/jieba-rs",
                    }
                    for name in ("jieba-macros", "jieba-rs")
                ],
            }
            correction = LicenseCorrection(
                crates=("jieba-macros", "jieba-rs"),
                license_crate="jieba-rs",
                license_path="third-party-licenses/jieba.LICENSE",
                license_name="MIT License (jieba-rs workspace)",
                anchor="mit-jieba-rs-workspace",
                license_from_repository=True,
                expected_versions=(
                    ("jieba-macros", "0.10.3"),
                    ("jieba-rs", "0.10.3"),
                ),
            )

            result = correction_html(root, metadata, correction)

            self.assertIn("Jieba workspace license", result)
            self.assertIn("jieba-macros 0.10.3", result)
            self.assertIn("jieba-rs 0.10.3", result)

    def test_repository_license_correction_rejects_unverified_version(self) -> None:
        root = Path(__file__).resolve().parents[2]
        correction = next(
            correction
            for correction in PYTHON_MIT_CORRECTIONS
            if correction.crates == ("jieba-macros", "jieba-rs")
        )
        metadata = {
            "normal_packages": {
                ("jieba-macros", "9.9.9"),
                ("jieba-rs", "9.9.9"),
            },
            "packages": [
                {
                    "name": name,
                    "version": "9.9.9",
                    "manifest_path": str(root / name / "Cargo.toml"),
                    "repository": "https://example.com/jieba-rs",
                }
                for name in ("jieba-macros", "jieba-rs")
            ],
        }

        with self.assertRaisesRegex(
            RuntimeError,
            r"expected jieba-macros 0\.10\.3, found 9\.9\.9",
        ):
            correction_html(root, metadata, correction)

    def test_correction_supports_multiple_versions_with_same_license(self) -> None:
        with tempfile.TemporaryDirectory() as temp:
            root = Path(temp)
            packages = []
            normal_packages = set()
            for version in ("0.7.0", "0.11.0"):
                package_dir = root / f"tantivy-common-{version}"
                package_dir.mkdir()
                manifest = package_dir / "Cargo.toml"
                manifest.write_text("[package]\n", encoding="utf-8")
                (package_dir / "LICENSE").write_text(
                    "Shared Tantivy license\n",
                    encoding="utf-8",
                )
                packages.append(
                    {
                        "name": "tantivy-common",
                        "version": version,
                        "manifest_path": str(manifest),
                        "repository": "https://example.com/tantivy",
                    }
                )
                normal_packages.add(("tantivy-common", version))
            metadata = {
                "normal_packages": normal_packages,
                "packages": packages,
            }
            correction = LicenseCorrection(
                crates=("tantivy-common",),
                license_crate="tantivy-common",
                license_path="LICENSE",
                license_name="MIT License (Tantivy workspace)",
                anchor="mit-tantivy-workspace",
            )

            result = correction_html(root, metadata, correction)

            self.assertIn("tantivy-common 0.7.0", result)
            self.assertIn("tantivy-common 0.11.0", result)
            self.assertEqual(result.count("Shared Tantivy license"), 1)

    def test_jieba_license_source_is_documented_and_header_exempt(self) -> None:
        root = Path(__file__).resolve().parents[2]
        license_path = "third-party-licenses/jieba-rs-0.10.3.LICENSE"
        license_config = (root / ".licenserc.yaml").read_text(encoding="utf-8")
        third_party_readme = (
            root / "third-party-licenses" / "README.md"
        ).read_text(encoding="utf-8")

        self.assertIn(f'"{license_path}"', license_config)
        self.assertIn(
            "messense/jieba-rs/blob/"
            "c62e0df1f9dcc2cc1e014711c5aa4561ae260538/LICENSE",
            third_party_readme,
        )


class ResolvedPackagesTest(unittest.TestCase):
    def test_uses_normal_cargo_tree_packages(self) -> None:
        metadata = {
            "normal_packages": {("root", "1.0.0"), ("required", "2.0.0")},
            "packages": [
                {"name": "root", "version": "1.0.0"},
                {"name": "required", "version": "2.0.0"},
                {"name": "optional", "version": "3.0.0"},
            ],
        }
        self.assertEqual(
            [package["name"] for package in resolved_packages(metadata)],
            ["root", "required"],
        )

    def test_notice_uses_only_normal_dependencies(self) -> None:
        with tempfile.TemporaryDirectory() as temp:
            root = Path(temp)
            (root / "NOTICE").write_text("Root notice\n", encoding="utf-8")
            packages = []
            for name, notice in (
                ("required", "Required dependency notice\n"),
                ("optional", "Optional dependency notice\n"),
            ):
                package_dir = root / name
                package_dir.mkdir()
                manifest = package_dir / "Cargo.toml"
                manifest.write_text("[package]\n", encoding="utf-8")
                (package_dir / "NOTICE").write_text(notice, encoding="utf-8")
                packages.append(
                    {
                        "name": name,
                        "version": "1.0.0",
                        "manifest_path": str(manifest),
                    }
                )

            metadata = {
                "normal_packages": {("required", "1.0.0")},
                "packages": packages,
            }
            notice = binary_notice(root, [metadata])
            self.assertIn("Required dependency notice", notice)
            self.assertNotIn("Optional dependency notice", notice)


if __name__ == "__main__":
    unittest.main()
