"""Archive target declarations retain actual App Group values without guessing."""
import plistlib
import tempfile
import unittest
from pathlib import Path
from unittest import mock

import app_groups
import prepare_signing


class GroupRequirementsTest(unittest.TestCase):
    def test_parent_and_extension_receive_their_declared_groups(self):
        with tempfile.TemporaryDirectory() as directory:
            root = Path(directory)
            targets = []
            for name, groups in [("App", ["group.example.shared", "group.example.app"]),
                                 ("Broadcast", ["group.example.shared"])]:
                filename = name + ".entitlements"
                (root / filename).write_bytes(plistlib.dumps({app_groups.ENTITLEMENT: groups}))
                targets.append({"name": name, "bundle_id": name, "entitlements": filename})
            self.assertEqual(app_groups.declared_by_bundle(str(root / "App.xcodeproj"), targets), {
                "App": {"group.example.shared", "group.example.app"}, "Broadcast": {"group.example.shared"},
            })

    def test_unresolved_or_malformed_values_fail_before_signing(self):
        values = ["group.example", ["$(GROUP_ID)"], ["group.example.*"], [1], ["group..example"]]
        with tempfile.TemporaryDirectory() as directory:
            root = Path(directory)
            targets = [{"bundle_id": "App", "entitlements": "App.entitlements"}]
            for value in values:
                with self.subTest(value=value):
                    (root / "App.entitlements").write_bytes(plistlib.dumps({app_groups.ENTITLEMENT: value}))
                    with self.assertRaisesRegex(SystemExit, "literal group identifiers"):
                        app_groups.declared_by_bundle(str(root / "App.xcodeproj"), targets)

    def test_unreadable_declaration_is_not_an_empty_requirement(self):
        with tempfile.TemporaryDirectory() as directory:
            with self.assertRaisesRegex(SystemExit, "unreadable"):
                app_groups.declared_by_bundle(str(Path(directory) / "App.xcodeproj"), [
                    {"bundle_id": "App", "entitlements": "missing.entitlements"},
                ])

    def test_unrelated_targets_and_empty_groups_need_no_assignment(self):
        self.assertEqual(app_groups.declared_by_bundle("App.xcodeproj", [{"bundle_id": "App"}]), {})
        app_groups.validate_requirements({"App": {app_groups.ENTITLEMENT}}, {"App": set()})

    def test_key_only_legacy_requirement_is_refused(self):
        with self.assertRaisesRegex(SystemExit, "declared identifier values"):
            app_groups.validate_requirements({"App": {app_groups.ENTITLEMENT}}, {})

    def test_malformed_or_wildcard_profile_does_not_grant_group(self):
        required = {"group.example.shared"}
        for value in [None, "group.example.shared", ["group.*"], [1], ["group.example.other"]]:
            with self.subTest(value=value):
                self.assertEqual(app_groups.missing({app_groups.ENTITLEMENT: value}, required), required)

    def test_prepare_signing_forwards_values_to_profile_provisioning(self):
        with tempfile.TemporaryDirectory() as directory:
            root = Path(directory)
            (root / "App.entitlements").write_bytes(plistlib.dumps({
                app_groups.ENTITLEMENT: ["group.example.shared"],
            }))
            targets = [{"name": "App", "bundle_id": "App", "config_ids": ["R"],
                        "entitlements": "App.entitlements"}]
            env = {"RUNNER_TEMP": directory, "PROJECT": str(root / "App.xcodeproj"),
                   "ASC_KEY_ID": "K", "ASC_ISSUER_ID": "I", "ASC_KEY_PATH": "unused"}
            with mock.patch.dict(prepare_signing.os.environ, env, clear=True), \
                    mock.patch.object(prepare_signing, "make_jwt", return_value="test"), \
                    mock.patch.object(prepare_signing, "discover_signable_targets", return_value=targets), \
                    mock.patch.object(prepare_signing, "_load_signing_identity", return_value=("C", b"p12", "", True)), \
                    mock.patch.object(
                        prepare_signing, "provision_all_bundles", return_value=([], "TEAM")) as provision, \
                    mock.patch.object(prepare_signing, "patch_project_signing"), \
                    mock.patch.object(prepare_signing, "setup_keychain"):
                prepare_signing.main()
            self.assertEqual(provision.call_args.kwargs["app_groups_by_bundle"], {
                "App": {"group.example.shared"},
            })


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