"""Store evidence must exist before configuration enables compilation or version writes."""
import io
import hashlib
import json
import os
from pathlib import Path
import sys
import subprocess
import tempfile
import unittest
from unittest import mock

sys.path.insert(0, str(Path(__file__).resolve().parent))
import resolve_android as resolve
from play_inventory import PlayInventory


class ResolvePlayReadinessTests(unittest.TestCase):
    def setUp(self):
        self.root = Path(self.enterContext(tempfile.TemporaryDirectory())).resolve()
        (self.root / "creds").mkdir()
        (self.root / "pubspec.yaml").write_text("name: fixture\n")
        app = self.root / "android/app"
        app.mkdir(parents=True)
        (app / "build.gradle").write_text('android { defaultConfig { applicationId "com.example.app" } }')
        (self.root / "creds/key.jks").write_bytes(b"fixture")
        (self.root / "creds/signing.properties").write_text(
            "storeFile=key.jks\nstorePassword=fixture\nkeyPassword=fixture\nkeyAlias=fixture\n")
        (self.root / "creds/account.json").write_text(json.dumps(
            {"type": "service_account", "client_email": "fixture@example.invalid", "private_key": "fixture"}))
        self.env_file, self.outputs = self.root / "env", self.root / "output"
        self.enterContext(mock.patch.dict(os.environ, {"GITHUB_WORKSPACE": str(self.root),
            "GITHUB_OUTPUT": str(self.outputs), "GITHUB_ENV": str(self.env_file), "GITHUB_RUN_NUMBER": "1",
            "INPUT_BUILD_NUMBER": "", "INPUT_PACKAGE_NAME": "", "INPUT_BUILD_NAME": ""}))
        self.read = self.enterContext(mock.patch.object(
            resolve, "read_inventory", return_value=PlayInventory(True, 173)))
        self.prepare = self.enterContext(mock.patch.object(resolve, "prepare_flutter_toolchain"))
        self.enterContext(mock.patch("sys.stdout", new_callable=io.StringIO))

    def test_known_inventory_is_read_once_and_prevents_a_version_collision(self):
        resolve.main()
        self.read.assert_called_once_with("com.example.app", self.root / "creds/account.json")
        self.assertIn("build_number=174\n", self.outputs.read_text())
        self.assertIn("play_api_ready=true\n", self.outputs.read_text())
        self.assertIn("ANDROID_PLAY_API_READY=true\n", self.env_file.read_text())
        self.prepare.assert_called_once_with(self.root)

    def test_owner_package_is_local_and_preserves_an_explicit_identity(self):
        for path in (self.root / "creds").iterdir():
            if path.suffix != ".json":
                path.unlink()
        resolve.owner_package()
        expected = hashlib.sha256(b"fixture@example.invalid").hexdigest()
        self.assertEqual(json.loads(sys.stdout.getvalue()), {"package": "com.example.app", "principal_sha256": expected})
        self.read.assert_not_called()
        self.prepare.assert_not_called()
        self.assertFalse(self.env_file.exists())
        self.assertFalse(self.outputs.exists())
        with mock.patch.dict(os.environ, {"INPUT_PACKAGE_NAME": "org.owned.override"}):
            resolve.owner_package()
        self.assertEqual(json.loads(sys.stdout.getvalue().splitlines()[-1])["package"], "org.owned.override")
        self.assertNotIn("fixture@example.invalid", sys.stdout.getvalue())

    def test_actual_local_identity_child_emits_only_package_and_principal_hash(self):
        result = subprocess.run([sys.executable, str(Path(resolve.__file__)), "--owner-package"],
                                capture_output=True, text=True, timeout=10, check=False)
        self.assertEqual(result.returncode, 0, result.stderr)
        self.assertEqual(json.loads(result.stdout), {"package": "com.example.app",
            "principal_sha256": hashlib.sha256(b"fixture@example.invalid").hexdigest()})
        self.assertNotIn("fixture@example.invalid", result.stdout + result.stderr)

    def test_absent_package_keeps_configuration_for_a_first_release_artifact(self):
        self.read.return_value = PlayInventory(False, None)
        resolve.main()
        self.assertIn("build_number=1\n", self.outputs.read_text())
        self.assertIn("play_api_ready=false\n", self.outputs.read_text())
        self.assertIn("ANDROID_SIGNING_PROPERTIES=", self.env_file.read_text())
        self.prepare.assert_called_once()

    def test_indeterminate_inventory_never_enables_a_build_or_mutates_toolchain(self):
        self.read.side_effect = SystemExit("classified transport failure")
        with self.assertRaises(SystemExit):
            resolve.main()
        self.prepare.assert_not_called()
        self.assertFalse(self.env_file.exists())
        self.assertFalse(self.outputs.exists())

    def test_explicit_version_is_preserved_after_the_same_readiness_check(self):
        with mock.patch.dict(os.environ, {"INPUT_BUILD_NUMBER": "200"}):
            resolve.main()
        self.read.assert_called_once()
        self.assertIn("build_number=200\n", self.outputs.read_text())

    def test_explicit_version_does_not_bypass_a_failed_store_read(self):
        self.read.side_effect = SystemExit("classified auth failure")
        with mock.patch.dict(os.environ, {"INPUT_BUILD_NUMBER": "200"}), self.assertRaises(SystemExit):
            resolve.main()
        self.prepare.assert_not_called()
        self.assertFalse(self.outputs.exists())

    def test_exhausted_version_space_does_not_overflow_or_fall_back(self):
        self.read.return_value = PlayInventory(True, 2_100_000_000)
        with self.assertRaises(SystemExit):
            resolve.main()
        self.assertFalse(self.outputs.exists())


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