"""Execute the shipped Flutter preparation shell with real guards and offline tool fixtures."""
import json
import os
from pathlib import Path
import subprocess
import sys
import tempfile
import textwrap
import unittest

import native_bundle
from test_native_dependency_guard import project

REPO = Path(__file__).resolve().parents[2]


class FlutterPreparationTests(unittest.TestCase):
    def setUp(self):
        temporary = tempfile.TemporaryDirectory(prefix="flutter-native-preparation-")
        self.addCleanup(temporary.cleanup)
        self.root = Path(temporary.name).resolve()
        project(self.root, "ios/Runner.xcodeproj")
        workspace = self.root / "ios/Runner.xcworkspace"
        workspace.mkdir()
        (workspace / "contents.xcworkspacedata").write_text(
            '<Workspace><FileRef location="group:Runner.xcodeproj"/></Workspace>')
        for name in ("Podfile", "Podfile.lock"):
            (self.root / "ios" / name).write_text("fixture lock\n")
        (self.root / "pubspec.yaml").write_text("name: fixture\n")
        for argv in (["git", "init", "-q"], ["git", "add", "."],
                     ["git", "-c", "user.name=Fixture", "-c", "user.email=fixture@example.invalid",
                      "commit", "-qm", "Freeze native fixture"]):
            subprocess.run(argv, cwd=self.root, capture_output=True, check=True)
        actions = self.root / ".github/actions"
        actions.mkdir(parents=True)
        (actions / "swift-app").symlink_to(REPO / "action", target_is_directory=True)
        (actions / "android-app").symlink_to(REPO / "android-action", target_is_directory=True)
        self.env = {**os.environ, "PATH": str(self.root) + os.pathsep + os.environ["PATH"],
                    "GITHUB_WORKSPACE": str(self.root), "RUNNER_TEMP": str(self.root / "scratch"),
                    "ONBOARDING_PUBLIC_ORIGIN": "https://flow.example.invalid"}
        self.install_tools()

    def executable(self, name, source):
        path = self.root / name
        path.write_text("#!" + sys.executable + "\n" + textwrap.dedent(source))
        path.chmod(0o700)

    def install_tools(self):
        self.executable("flutter", '''
            import json,sys
            from pathlib import Path
            if sys.argv[1:3] == ["pub", "get"]:
                folder=Path("ios/Flutter");folder.mkdir()
                (folder/"Generated.xcconfig").write_text("generated")
            elif sys.argv[1:3] == ["build", "ios"]:
                Path("configuration.json").write_text(json.dumps(sys.argv[1:]))
                assert Path("ios/pods-ready").is_file(), "Configuration reached before locked Pods"
            else:
                raise SystemExit(9)
        ''')
        self.executable("pod", '''
            import json,os,sys
            from pathlib import Path
            assert Path("Flutter/Generated.xcconfig").is_file()
            assert sys.argv[1:] == ["install", "--deployment", "--no-repo-update"]
            Path("pod-called").touch()
            if os.environ.get("FAIL_PODS"):
                raise SystemExit(7)
            Path("pods-ready").touch()
        ''')

    def run_preparation(self):
        body = (REPO / "templates/deploy.yml").read_text().split(
            "- name: Prepare Flutter iOS project\n", 1)[1].split("\n      - ", 1)[0]
        script = textwrap.dedent(body.split("        run: |\n", 1)[1])
        result = subprocess.run([sys.executable, native_bundle.__file__, "--folder", "ios",
                                 "--", "bash", "--noprofile", "--norc", "-e", "-o", "pipefail", "-c", script],
                                cwd=self.root, env=self.env, capture_output=True, text=True, timeout=20)
        return result

    def test_locked_pods_precede_configuration_without_losing_release_arguments(self):
        result = self.run_preparation()
        self.assertEqual(result.returncode, 0, result.stdout)
        self.assertTrue((self.root / "ios/pod-called").exists())
        args = json.loads((self.root / "configuration.json").read_text())
        for arg in ("--release", "--no-codesign", "--config-only", "--obfuscate",
                    "--split-debug-info=" + str(self.root / "scratch/dart-symbols/ios"),
                    "ONBOARDING_PUBLIC_ORIGIN=https://flow.example.invalid"):
            self.assertIn(arg, args)
        self.assertEqual((self.root / "ios/Podfile.lock").read_text(), "fixture lock\n")

    def test_failed_locked_install_stops_before_flutter_configuration(self):
        self.env["FAIL_PODS"] = "1"
        self.assertNotEqual(self.run_preparation().returncode, 0)
        self.assertTrue((self.root / "ios/pod-called").exists())
        self.assertFalse((self.root / "configuration.json").exists())

    def test_remote_package_graph_refuses_before_configuration_without_podfile(self):
        (self.root / "ios/Podfile").unlink()
        project(self.root, "ios/Runner.xcodeproj", {"remote": {"isa": "XCRemoteSwiftPackageReference"}})
        self.assertNotEqual(self.run_preparation().returncode, 0)
        self.assertFalse((self.root / "ios/pod-called").exists())
        self.assertFalse((self.root / "configuration.json").exists())
