"""Promotion accepts only the same inputs and retained bytes; candidate version planning is read-only."""
import json
import os
from pathlib import Path
import tempfile
import unittest
from unittest.mock import patch

import mobile_artifact_files as artifact
import mobile_candidate_context
import mmv_floor_check
import mmv_decide_create
import manage_marketing_version as mmv


class ArtifactTests(unittest.TestCase):
    def setUp(self):
        self.temporary = tempfile.TemporaryDirectory()
        self.addCleanup(self.temporary.cleanup)
        self.root = Path(self.temporary.name)
        self.event = self.root / "event.json"
        self.event.write_text(json.dumps({"repository": {"default_branch": "main"}, "pull_request": {
            "head": {"ref": "task/fix", "sha": "b" * 40, "repo": {"full_name": "owned/app"}},
            "base": {"ref": "main"}}}))
        environment = {"RUNNER_TEMP": str(self.root), "GITHUB_WORKSPACE": str(self.root),
                       "GITHUB_EVENT_PATH": str(self.event),
                       "GITHUB_REPOSITORY": "owned/app", "GITHUB_SHA": "a" * 40,
                       "GITHUB_RUN_ID": "123", "GITHUB_RUN_ATTEMPT": "1",
                       "MOBILE_ARTIFACT_FINGERPRINT": "f" * 64, "BUILD_NUMBER": "42",
                       "CFG_BUNDLE_ID": "com.owned.app", "CFG_TEAM_ID": "AAAAAAAAAA", "CFG_APP_STORE_APPLE_ID": "123",
                       "MARKETING_VERSION_AUTO_BUMP": "rollover",
                       "MARKETING_VERSION": "1.0.1", "SOURCE_MARKETING_VERSION": "1.0.0",
                       "MOBILE_CANDIDATE_BUILD": "true", "GITHUB_EVENT_NAME": "pull_request"}
        self.environment = patch.dict(os.environ, environment)
        self.environment.start()
        self.addCleanup(self.environment.stop)

    def packed(self):
        export = self.root / "export"
        export.mkdir()
        (export / "App.ipa").write_bytes(b"signed-binary-fixture")
        symbols = self.root / "dart-symbols/ios"
        symbols.mkdir(parents=True)
        (symbols / "app.symbols").write_bytes(b"the-exact-build-symbols")
        directory = self.root / "retained"
        directory.mkdir()
        artifact.pack("ios", directory)
        return directory

    def test_pack_excludes_credentials_and_retains_binary_version_and_symbols(self):
        creds = self.root / "creds"
        creds.mkdir()
        (creds / "private.p8").write_text("do-not-retain")
        directory = self.packed()
        value = artifact.verify("ios", directory)
        self.assertEqual(value["head_sha"], "b" * 40)
        self.assertEqual(value["environment"]["BUILD_NUMBER"], "42")
        self.assertEqual(set(value["files"]), {"export/app.ipa", "dart-symbols/ios/app.symbols"})
        self.assertNotIn("do-not-retain", (directory / "mobile-binary.json").read_text())
        (creds / "private.p8").write_text("rotated-account-material")
        with self.assertRaises(ValueError):
            artifact.verify("ios", directory)

    def test_changed_binary_or_inputs_cannot_be_promoted(self):
        directory = self.packed()
        with patch.dict(os.environ, {"MOBILE_ARTIFACT_FINGERPRINT": "e" * 64}):
            with self.assertRaises(ValueError):
                artifact.verify("ios", directory)
        with patch.dict(os.environ, {"GITHUB_REPOSITORY": "foreign/app"}):
            with self.assertRaises(ValueError):
                artifact.verify("ios", directory)
        (directory / "export/app.ipa").write_bytes(b"different-bytes")
        with self.assertRaises(ValueError):
            artifact.verify("ios", directory)

    def test_candidate_predicts_floor_without_source_or_store_writes(self):
        with patch.object(mmv_floor_check.version_utils, "write_marketing_version") as write:
            value = mmv_floor_check.maybe_auto_bump("1.0.0", "1.0.0")
        self.assertEqual(value, "1.0.1")
        write.assert_not_called()
        with patch.object(mmv_decide_create, "assert_target_meets_floor", return_value="1.0.1"), \
                patch.object(mmv, "create_or_reuse_with_stale") as create:
            result = mmv_decide_create.decide_create("1.0.1", [], "123", "test-token")
        self.assertEqual(result, {"decision": "PREPARE", "versionString": "1.0.1", "appStoreVersionId": ""})
        create.assert_not_called()

    def test_fork_cannot_use_candidate_version_mode(self):
        event = json.loads(self.event.read_text())
        event["pull_request"]["head"]["repo"]["full_name"] = "foreign/app"
        self.event.write_text(json.dumps(event))
        with self.assertRaises(ValueError):
            mobile_candidate_context.active()
