#!/usr/bin/env python3
"""
Integration tests for read_config (post-YAML-removal). Verify that:
- resolve_xcode runs auto_detect when input is empty.
- resolve_xcode honors INPUT_* overrides.
- resolve_app calls auto_detect_bundle_id when input is empty.
- Source labels for log output are auditable.
"""

from __future__ import annotations

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

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

import auto_detect  # noqa: E402
import read_config  # noqa: E402


def _empty_inputs() -> dict:
    return {
        "PROJECT": "",
        "WORKSPACE": "",
        "SCHEME": "",
        "CONFIGURATION": "",
        "PROFILE_NAME": "",
        "BUNDLE_ID": "",
        "TEAM_ID": "",
        "APP_STORE_APPLE_ID": "",
        "USES_NON_EXEMPT": "",
        "WHATS_NEW": "",
        "LOCALE": "",
        "RUN_TESTS": "",
        "TEST_COMMAND": "",
        "TEST_DESTINATION": "",
    }


class ResolveXcodeAutoDetectTests(unittest.TestCase):
    def setUp(self):
        auto_detect._clear_caches()

    def test_auto_detect_used_when_inputs_empty(self):
        workspace = Path("/fake/workspace")
        with mock.patch.object(
            auto_detect,
            "auto_detect_project",
            return_value=("MyApp.xcodeproj", ""),
        ) as m_proj, mock.patch.object(
            auto_detect, "auto_detect_scheme", return_value="MyApp",
        ) as m_scheme:
            xc = read_config.resolve_xcode(workspace, _empty_inputs())

        m_proj.assert_called_once_with(workspace)
        m_scheme.assert_called_once()
        self.assertEqual(xc["project"], ("MyApp.xcodeproj", "auto-detect"))
        self.assertEqual(xc["workspace"], ("", "empty"))
        self.assertEqual(xc["scheme"], ("MyApp", "auto-detect"))
        self.assertEqual(xc["configuration"], ("Release", "default"))
        self.assertEqual(xc["profile_name"], ("MyApp CI", "default"))

    def test_input_value_wins_over_auto_detect(self):
        workspace = Path("/fake/workspace")
        inputs = _empty_inputs()
        inputs["PROJECT"] = "OverrideProject.xcodeproj"
        inputs["SCHEME"] = "OverrideScheme"
        with mock.patch.object(
            auto_detect, "auto_detect_project",
            return_value=("Auto.xcodeproj", ""),
        ) as m_proj, mock.patch.object(
            auto_detect, "auto_detect_scheme", return_value="AutoScheme",
        ) as m_scheme:
            xc = read_config.resolve_xcode(workspace, inputs)
        m_proj.assert_not_called()
        m_scheme.assert_not_called()
        self.assertEqual(xc["project"], ("OverrideProject.xcodeproj", "input"))
        self.assertEqual(xc["scheme"], ("OverrideScheme", "input"))

    def test_workspace_input_wins(self):
        workspace = Path("/fake/workspace")
        inputs = _empty_inputs()
        inputs["WORKSPACE"] = "MyApp.xcworkspace"
        with mock.patch.object(
            auto_detect, "auto_detect_scheme", return_value="MyApp",
        ):
            xc = read_config.resolve_xcode(workspace, inputs)
        self.assertEqual(xc["workspace"], ("MyApp.xcworkspace", "input"))
        self.assertEqual(xc["project"], ("", "empty"))

    def test_workspace_auto_detect_preferred_over_xcodeproj(self):
        workspace = Path("/fake/workspace")
        with mock.patch.object(
            auto_detect, "auto_detect_project",
            return_value=("", "MyApp.xcworkspace"),
        ), mock.patch.object(
            auto_detect, "auto_detect_scheme", return_value="MyApp",
        ):
            xc = read_config.resolve_xcode(workspace, _empty_inputs())
        self.assertEqual(xc["workspace"], ("MyApp.xcworkspace", "auto-detect"))
        self.assertEqual(xc["project"], ("", "empty"))

    def test_scheme_empty_when_auto_detect_fails(self):
        workspace = Path("/fake/workspace")
        with mock.patch.object(
            auto_detect, "auto_detect_project",
            return_value=("MyApp.xcodeproj", ""),
        ), mock.patch.object(
            auto_detect, "auto_detect_scheme", return_value=None,
        ):
            xc = read_config.resolve_xcode(workspace, _empty_inputs())
        self.assertEqual(xc["scheme"], ("", "empty"))

    def test_configuration_input_wins_over_default(self):
        workspace = Path("/fake/workspace")
        inputs = _empty_inputs()
        inputs["CONFIGURATION"] = "Debug"
        with mock.patch.object(
            auto_detect, "auto_detect_project", return_value=("X.xcodeproj", ""),
        ), mock.patch.object(
            auto_detect, "auto_detect_scheme", return_value="X",
        ):
            xc = read_config.resolve_xcode(workspace, inputs)
        self.assertEqual(xc["configuration"], ("Debug", "input"))


class ResolveAppAutoDetectTests(unittest.TestCase):
    def setUp(self):
        auto_detect._clear_caches()

    def _xc(self, **over) -> dict:
        base = {
            "project": "MyApp.xcodeproj",
            "workspace": "",
            "scheme": "MyApp",
            "configuration": "Release",
        }
        base.update(over)
        return base

    def test_auto_detects_bundle_when_input_empty(self):
        creds = {"key_id": "K", "issuer_id": "I", "key_path": "/tmp/k.p8"}
        scripts_dir = Path(__file__).resolve().parent
        workspace = Path("/fake/workspace")
        with mock.patch.object(
            auto_detect,
            "auto_detect_bundle_id",
            return_value="com.example.myapp",
        ) as m_bundle, mock.patch.object(
            read_config,
            "derive_team_if_empty",
            return_value=("TEAM123456", "derived_from_asc_key"),
        ), mock.patch.object(
            read_config, "lookup_app_id_via_api", return_value="1234567890",
        ):
            app = read_config.resolve_app(
                _empty_inputs(), creds, scripts_dir,
                workspace=workspace, xcode=self._xc(),
            )
        m_bundle.assert_called_once()
        self.assertEqual(app["bundle_id"], ("com.example.myapp", "auto-detect"))
        self.assertEqual(app["team_id"], ("TEAM123456", "derived_from_asc_key"))
        self.assertEqual(app["app_store_apple_id"], ("1234567890", "api-lookup"))

    def test_input_bundle_wins_over_auto_detect(self):
        creds = {"key_id": "K", "issuer_id": "I", "key_path": "/tmp/k.p8"}
        scripts_dir = Path(__file__).resolve().parent
        workspace = Path("/fake/workspace")
        inputs = _empty_inputs()
        inputs["BUNDLE_ID"] = "com.input.bundle"
        with mock.patch.object(
            auto_detect, "auto_detect_bundle_id", return_value="com.auto.different",
        ) as m_bundle, mock.patch.object(
            read_config, "derive_team_if_empty",
            return_value=("T", "derived_from_asc_key"),
        ), mock.patch.object(
            read_config, "lookup_app_id_via_api", return_value="1",
        ):
            app = read_config.resolve_app(
                inputs, creds, scripts_dir,
                workspace=workspace, xcode=self._xc(),
            )
        m_bundle.assert_not_called()
        self.assertEqual(app["bundle_id"], ("com.input.bundle", "input"))

    def test_input_team_id_wins_over_derive(self):
        creds = {"key_id": "K", "issuer_id": "I", "key_path": "/tmp/k.p8"}
        scripts_dir = Path(__file__).resolve().parent
        workspace = Path("/fake/workspace")
        inputs = _empty_inputs()
        inputs["TEAM_ID"] = "INPUTTEAM1"
        inputs["BUNDLE_ID"] = "com.x.y"
        with mock.patch.object(
            read_config, "derive_team_if_empty",
        ) as m_derive, mock.patch.object(
            read_config, "lookup_app_id_via_api", return_value="1",
        ):
            app = read_config.resolve_app(
                inputs, creds, scripts_dir,
                workspace=workspace, xcode=self._xc(),
            )
        m_derive.assert_not_called()
        self.assertEqual(app["team_id"], ("INPUTTEAM1", "input"))

    def test_input_apple_id_skips_api_lookup(self):
        creds = {"key_id": "K", "issuer_id": "I", "key_path": "/tmp/k.p8"}
        scripts_dir = Path(__file__).resolve().parent
        workspace = Path("/fake/workspace")
        inputs = _empty_inputs()
        inputs["APP_STORE_APPLE_ID"] = "9999999"
        inputs["BUNDLE_ID"] = "com.x.y"
        with mock.patch.object(
            read_config, "derive_team_if_empty",
            return_value=("T", "derived_from_asc_key"),
        ), mock.patch.object(
            read_config, "lookup_app_id_via_api",
        ) as m_lookup:
            app = read_config.resolve_app(
                inputs, creds, scripts_dir,
                workspace=workspace, xcode=self._xc(),
            )
        m_lookup.assert_not_called()
        self.assertEqual(app["app_store_apple_id"], ("9999999", "input"))


class DefaultsTests(unittest.TestCase):
    """Defaults emitted when nothing comes from inputs."""

    def test_resolve_testflight_returns_default_whats_new_and_locale(self):
        tf = read_config.resolve_testflight(_empty_inputs())
        self.assertEqual(tf["whats_new"][1], "default")
        self.assertIn("Improved performance", tf["whats_new"][0])
        self.assertEqual(tf["locale"], ("en-US", "default"))

    def test_resolve_testflight_input_wins(self):
        inputs = _empty_inputs()
        inputs["WHATS_NEW"] = "release notes here"
        inputs["LOCALE"] = "fr-FR"
        tf = read_config.resolve_testflight(inputs)
        self.assertEqual(tf["whats_new"], ("release notes here", "input"))
        self.assertEqual(tf["locale"], ("fr-FR", "input"))

    def test_resolve_tests_defaults_to_false(self):
        tests = read_config.resolve_tests(_empty_inputs())
        self.assertEqual(tests["run_tests"], ("false", "default"))
        self.assertEqual(tests["test_command"], ("", "empty"))
        self.assertEqual(tests["test_destination"], ("", "empty"))

    def test_resolve_ios_defaults_to_false(self):
        ios = read_config.resolve_ios(_empty_inputs())
        self.assertEqual(ios["uses_non_exempt"], ("false", "default"))


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