#!/usr/bin/env python3
"""Cached-profile reuse must account for the app's entitlements.

The manifest records a profile's UUID and expiry but nothing about the
capabilities Apple baked into it. Without checking, a profile issued before a
capability was enabled — by this action or by hand in the developer portal —
is reused forever and the archive keeps failing on a capability the App ID
already has.
"""

from __future__ import annotations

import tempfile
import unittest
from datetime import datetime, timedelta, timezone
from pathlib import Path
from unittest import mock

import capabilities
import profile_manager


APP_ATTEST = capabilities.APP_ATTEST_ENTITLEMENT


class CachedProfileEntitlementsTest(unittest.TestCase):
    def setUp(self) -> None:
        self.temp = tempfile.TemporaryDirectory()
        self.creds = Path(self.temp.name)
        self.uuid = "c981d698-f941-44ff-8ff2-5823b7851ac6"
        cached = mock.MagicMock()
        cached.uuid = self.uuid
        cached.name = "CI-com.gowalk.form"
        cached.filename = f"{self.uuid}.mobileprovision"
        cached.expiration = datetime.now(timezone.utc) + timedelta(days=300)
        self.cached = cached

    def tearDown(self) -> None:
        self.temp.cleanup()

    def _reuse(self, profile_entitlements: dict, required: set[str]):
        path = mock.MagicMock()
        path.read_bytes.return_value = b"der"
        with mock.patch.object(
                profile_manager.creds_store, "find_reusable_profile",
                return_value=self.cached), \
                mock.patch.object(
                    profile_manager.creds_store, "profile_path", return_value=path), \
                mock.patch.object(
                    profile_manager, "decode_profile_plist",
                    return_value={"Entitlements": profile_entitlements}), \
                mock.patch.object(
                    profile_manager, "install_profile",
                    return_value=(self.uuid, "TEAM123", None)):
            return profile_manager._try_reuse_cached(
                "com.gowalk.form", "CI-com.gowalk.form", "CERT",
                self.creds, {}, required,
            )

    def test_rejects_a_profile_issued_before_the_capability(self) -> None:
        result = self._reuse({"aps-environment": "production"}, {APP_ATTEST})

        self.assertIsNone(result)

    def test_reuses_a_profile_that_carries_the_entitlement(self) -> None:
        result = self._reuse(
            {"aps-environment": "production", APP_ATTEST: "production"},
            {APP_ATTEST, "aps-environment"},
        )

        self.assertIsNotNone(result)
        self.assertEqual(result[0], self.uuid)

    def test_ignores_entitlements_a_profile_never_mirrors(self) -> None:
        # keychain-access-groups needs no capability, and App Groups needs App
        # ID configuration this action does not perform — treating either as
        # missing would regenerate the profile on every run and fix nothing.
        result = self._reuse(
            {APP_ATTEST: "production"},
            {APP_ATTEST, "keychain-access-groups",
             "com.apple.security.application-groups"},
        )

        self.assertIsNotNone(result)

    def test_no_entitlements_declared_keeps_the_old_fast_path(self) -> None:
        result = self._reuse({}, set())

        self.assertIsNotNone(result)


class MissingProfileEntitlementsTest(unittest.TestCase):
    def test_reports_only_checkable_keys(self) -> None:
        self.assertEqual(
            capabilities.missing_profile_entitlements(
                {"aps-environment": "production"},
                {APP_ATTEST, "aps-environment", "keychain-access-groups"},
            ),
            {APP_ATTEST},
        )

    def test_empty_when_the_profile_covers_everything(self) -> None:
        self.assertEqual(
            capabilities.missing_profile_entitlements(
                {"aps-environment": "x", APP_ATTEST: "production"},
                {APP_ATTEST, "aps-environment"},
            ),
            set(),
        )


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