#!/usr/bin/env python3
"""Entitlements-path resolution in pbxproj_editor."""

import unittest
from pathlib import Path
from tempfile import TemporaryDirectory

from pbxproj_editor import _entitlements_from_configs, _expand_entitlements_refs


def _objects(value):
    return {"cfg": {"buildSettings": {"CODE_SIGN_ENTITLEMENTS": value}}}


class ExpandEntitlementsRefsTest(unittest.TestCase):
    def test_expands_target_name_both_spellings(self):
        self.assertEqual(
            _expand_entitlements_refs("$(TARGET_NAME)/$(TARGET_NAME).entitlements", "Blocker"),
            "Blocker/Blocker.entitlements",
        )
        self.assertEqual(
            _expand_entitlements_refs("${TARGET_NAME}/App.entitlements", "Blocker"),
            "Blocker/App.entitlements",
        )

    def test_srcroot_prefix_drops_out(self):
        self.assertEqual(
            _expand_entitlements_refs("$(SRCROOT)/App/App.entitlements", "App"),
            "App/App.entitlements",
        )

    def test_unknown_reference_is_left_alone(self):
        self.assertIn("$(CONFIGURATION)", _expand_entitlements_refs(
            "$(CONFIGURATION)/App.entitlements", "App"))


class EntitlementsFromConfigsTest(unittest.TestCase):
    def test_literal_path_is_returned_without_touching_disk(self):
        self.assertEqual(
            _entitlements_from_configs(_objects("App/App.entitlements"), ["cfg"],
                                       target_name="App", base=Path("/nonexistent")),
            "App/App.entitlements",
        )

    def test_target_name_reference_resolves_when_the_file_exists(self):
        with TemporaryDirectory() as tmp:
            root = Path(tmp)
            (root / "Blocker").mkdir()
            (root / "Blocker" / "Blocker.entitlements").write_text("<plist/>")
            self.assertEqual(
                _entitlements_from_configs(
                    _objects("$(TARGET_NAME)/$(TARGET_NAME).entitlements"), ["cfg"],
                    target_name="Blocker", base=root),
                "Blocker/Blocker.entitlements",
            )

    def test_expanded_path_that_does_not_exist_is_rejected(self):
        with TemporaryDirectory() as tmp:
            self.assertEqual(
                _entitlements_from_configs(
                    _objects("$(TARGET_NAME)/$(TARGET_NAME).entitlements"), ["cfg"],
                    target_name="Ghost", base=Path(tmp)),
                "",
            )

    def test_unresolvable_reference_is_skipped(self):
        self.assertEqual(
            _entitlements_from_configs(
                _objects("$(CONFIGURATION)/App.entitlements"), ["cfg"],
                target_name="App", base=Path("/tmp")),
            "",
        )

    def test_missing_setting_yields_empty(self):
        self.assertEqual(
            _entitlements_from_configs({"cfg": {"buildSettings": {}}}, ["cfg"]), "")




class ReleaseConfigPriorityTest(unittest.TestCase):
    """Release must drive provisioning: an entitlement wired into Debug only
    (the pre-grant CarPlay simulator-testing setup) must not require the
    capability on the App ID for the Release archive."""

    def _objects(self):
        return {
            "dbg": {
                "name": "Debug",
                "buildSettings": {"CODE_SIGN_ENTITLEMENTS": "App/Debug.entitlements"},
            },
            "rel": {"name": "Release", "buildSettings": {}},
            "prof": {"name": "Profile", "buildSettings": {}},
        }

    def test_debug_only_entitlements_are_invisible_when_release_has_none(self):
        # The pre-grant CarPlay setup: entitlements wired into Debug for the
        # simulator, Release deliberately unwired so the archive needs no
        # Apple-granted capability. The archive config decides — result "".
        objects = self._objects()
        self.assertEqual(
            _entitlements_from_configs(
                objects, ["dbg", "rel", "prof"], target_name="App",
                base=Path("/nonexistent")),
            "",
        )

    def test_all_configs_scanned_when_no_release_or_profile_exists(self):
        objects = {
            "a": {"name": "Staging", "buildSettings": {}},
            "b": {
                "name": "Custom",
                "buildSettings": {"CODE_SIGN_ENTITLEMENTS": "App/App.entitlements"},
            },
        }
        self.assertEqual(
            _entitlements_from_configs(
                objects, ["a", "b"], target_name="App", base=Path("/nonexistent")),
            "App/App.entitlements",
        )

    def test_release_wins_over_debug_regardless_of_order(self):
        objects = self._objects()
        objects["rel"]["buildSettings"]["CODE_SIGN_ENTITLEMENTS"] = (
            "App/Release.entitlements")
        for order in (["dbg", "rel", "prof"], ["rel", "dbg", "prof"]):
            self.assertEqual(
                _entitlements_from_configs(
                    objects, order, target_name="App", base=Path("/nonexistent")),
                "App/Release.entitlements",
            )

    def test_profile_is_the_archive_config_when_release_does_not_exist(self):
        objects = {
            "dbg": {
                "name": "Debug",
                "buildSettings": {"CODE_SIGN_ENTITLEMENTS": "App/Debug.entitlements"},
            },
            "prof": {
                "name": "Profile",
                "buildSettings": {"CODE_SIGN_ENTITLEMENTS": "App/Profile.entitlements"},
            },
        }
        self.assertEqual(
            _entitlements_from_configs(
                objects, ["dbg", "prof"], target_name="App",
                base=Path("/nonexistent")),
            "App/Profile.entitlements",
        )


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