#!/usr/bin/env python3
"""
Tests the integration wiring for auto-derived team_id:

  1. ``prepare_signing.main()`` no longer fails with "missing env var: TEAM_ID"
     when TEAM_ID is empty; it falls back to the team Apple assigned to the
     provisioning profile (already available via provision_all_bundles).

  2. ``cfg_resolve.derive_team_if_empty`` calls team_resolver.derive_team_id
     only when input+config yield an empty team, and emits a labeled source
     (``derived_from_asc_key``).

All ASC network calls are mocked.
"""

from __future__ import annotations

import os
import sys
import unittest
from pathlib import Path
from unittest import mock

sys.path.insert(0, str(Path(__file__).resolve().parent))

import cfg_resolve  # noqa: E402
import prepare_signing  # noqa: E402


class PrepareSigningTeamIdOptionalTests(unittest.TestCase):
    """prepare_signing.py: TEAM_ID env var must be optional now."""

    def test_main_runs_without_team_id_when_profiles_return_team(self):
        # Arrange: stub every external side-effect so main() can run end-to-end
        # without a real Xcode project or ASC API.
        env_patch = {
            "ASC_KEY_ID": "KID",
            "ASC_ISSUER_ID": "ISS",
            "ASC_KEY_PATH": "/tmp/fake.p8",
            "RUNNER_TEMP": "/tmp",
            "PROJECT": "/tmp/fake.xcodeproj",
            # TEAM_ID deliberately omitted — this is the bug we're fixing.
        }
        with mock.patch.dict(os.environ, env_patch, clear=True):
            with mock.patch.object(prepare_signing, "make_jwt", return_value="T"):
                with mock.patch.object(
                    prepare_signing,
                    "discover_signable_targets",
                    return_value=[
                        {
                            "name": "App",
                            "bundle_id": "com.example.app",
                            "config_ids": ["C1"],
                        }
                    ],
                ):
                    with mock.patch.object(
                        prepare_signing,
                        "_load_signing_identity",
                        return_value=("CERTID", b"FAKEP12", "password", False),
                    ):
                        with mock.patch.object(
                            prepare_signing,
                            "provision_all_bundles",
                            return_value=(
                                [("com.example.app", "CI-com.example.app", "UUID")],
                                "DERIVED123",  # effective_team from profile plist
                            ),
                        ):
                            with mock.patch.object(
                                prepare_signing, "patch_project_signing"
                            ):
                                with mock.patch.object(
                                    prepare_signing, "setup_keychain"
                                ):
                                    # Act: must not raise SystemExit.
                                    prepare_signing.main()

    def test_main_fails_with_actionable_message_when_nothing_yields_team(self):
        env_patch = {
            "ASC_KEY_ID": "KID",
            "ASC_ISSUER_ID": "ISS",
            "ASC_KEY_PATH": "/tmp/fake.p8",
            "RUNNER_TEMP": "/tmp",
            "PROJECT": "/tmp/fake.xcodeproj",
        }
        with mock.patch.dict(os.environ, env_patch, clear=True):
            with mock.patch.object(prepare_signing, "make_jwt", return_value="T"):
                with mock.patch.object(
                    prepare_signing,
                    "discover_signable_targets",
                    return_value=[
                        {"name": "A", "bundle_id": "b", "config_ids": ["C"]}
                    ],
                ):
                    with mock.patch.object(
                        prepare_signing,
                        "_load_signing_identity",
                        return_value=("CID", b"P12", "password", False),
                    ):
                        with mock.patch.object(
                            prepare_signing,
                            "provision_all_bundles",
                            # Profile with no TeamIdentifier — impossible in
                            # practice but we still need a graceful message.
                            return_value=([("b", "CI-b", "UUID")], ""),
                        ):
                            with mock.patch.object(
                                prepare_signing, "patch_project_signing"
                            ):
                                with self.assertRaises(SystemExit) as ctx:
                                    prepare_signing.main()
                                self.assertIn("team_id", str(ctx.exception).lower())


class CfgResolveDeriveTeamTests(unittest.TestCase):
    """cfg_resolve: derive_team_if_empty wires team_resolver into resolve_app."""

    def test_derive_not_called_when_team_from_config(self):
        """If config already supplies team_id, no API call is made."""
        with mock.patch("cfg_resolve.derive_team_id") as m_derive:
            result = cfg_resolve.derive_team_if_empty(
                team_val="ABC123",
                team_src="config",
                creds={"key_id": "K", "issuer_id": "I", "key_path": "/p"},
            )
        self.assertEqual(result, ("ABC123", "config"))
        m_derive.assert_not_called()

    def test_derive_called_when_team_empty_emits_labeled_source(self):
        """When team is empty, derive via ASC and label source."""
        with mock.patch("cfg_resolve.make_jwt", return_value="T"):
            with mock.patch(
                "cfg_resolve.derive_team_id", return_value="DERIVED7X8"
            ) as m_derive:
                result = cfg_resolve.derive_team_if_empty(
                    team_val="",
                    team_src="empty",
                    creds={"key_id": "K", "issuer_id": "I", "key_path": "/p"},
                )
        self.assertEqual(result, ("DERIVED7X8", "derived_from_asc_key"))
        m_derive.assert_called_once_with("T")

    def test_derive_keeps_empty_when_api_cant_determine(self):
        """If ASC lookup returns empty, keep empty — prepare_signing falls back."""
        with mock.patch("cfg_resolve.make_jwt", return_value="T"):
            with mock.patch("cfg_resolve.derive_team_id", return_value=""):
                result = cfg_resolve.derive_team_if_empty(
                    team_val="",
                    team_src="empty",
                    creds={"key_id": "K", "issuer_id": "I", "key_path": "/p"},
                )
        self.assertEqual(result, ("", "empty"))


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