"""Exercise real isolated installs and protected-remote publication without changing a live action."""
from pathlib import Path
import re
import subprocess
import textwrap
import unittest

from source_maintenance_fixture import MaintenanceFixture


class AutoupdateTests(MaintenanceFixture):
    def setUp(self):
        super().setUp()
        self.remote_base = self.base
        self.action = self.repo / ".github/actions/android-app/flutter-setup/action.yml"
        self.marker = self.repo / ".github/actions/swift-app/.daemux-version"
        self.action.parent.mkdir(parents=True)
        self.marker.parent.mkdir(parents=True)
        self.action.write_text("runs:\n  using: composite\n  steps:\n    - run: old\n      shell: bash\n")
        self.marker.write_text("1.2.3")
        self.git("add", ".github")
        self.git("commit", "-m", "Existing action")
        self.base = self.git("rev-parse", "HEAD").strip()
        self.env["GITHUB_SHA"] = self.base
        config = self.root / "checkout-auth.config"
        self.git("config", "--file", str(config), "fixture.token", "worktree-allowed")
        self.git("config", "includeIf.gitdir:" + str((self.repo / ".git").resolve()) + "/worktrees/*.path",
                 str(config))
        self.scratch = self.root / "runner-temp"
        self.scratch.mkdir()
        self.env["RUNNER_TEMP"] = str(self.scratch)
        binary = self.root / "bin"
        binary.mkdir()
        (binary / "npm").write_text("#!/bin/sh\nprintf '1.2.4\\n'\n")
        (binary / "npm").chmod(0o755)
        self.installer = binary / "npx"
        self.installer.write_text("#!/bin/sh\nset -eu\n"
            "[ \"$1\" = --yes ] && [ \"$2\" = gowalk-cicd@1.2.4 ]\n"
            "[ \"$(git config --get fixture.token)\" = worktree-allowed ]\n"
            "printf '1.2.4' > .github/actions/swift-app/.daemux-version\n"
            "printf '    - run: added\\n      shell: bash\\n' >> .github/actions/android-app/flutter-setup/action.yml\n"
            "mkdir -p .github/workflows .github/actions/swift-app/scripts/__pycache__\n"
            "printf 'never publish workflow' > .github/workflows/deploy.yml\n"
            "printf 'never publish bytecode' > .github/actions/swift-app/scripts/__pycache__/test.pyc\n"
            "exit ${INSTALL_EXIT:-0}\n")
        self.installer.chmod(0o755)
        self.env["PATH"] = str(binary) + ":" + self.env["PATH"]
        self.script = Path(__file__).with_name("autoupdate_check.sh")

    def assert_current_build_preserved(self, original, staged):
        self.assertEqual(self.action.read_bytes(), original)
        self.assertEqual(self.marker.read_text(), "1.2.3")
        self.assertEqual(self.git("diff", "--cached"), staged)
        self.assertEqual(self.git("rev-parse", "HEAD").strip(), self.base)
        self.assertEqual(self.git("ls-remote", "origin", "refs/heads/main").split()[0], self.remote_base)
        self.assertEqual(len(self.git("worktree", "list", "--porcelain").split("worktree ")) - 1, 1)
        self.assertEqual(list(self.scratch.iterdir()), [])

    def test_growing_composite_and_replay_preserve_current_build_and_publish_exact_update(self):
        self.stage()
        original, staged = self.action.read_bytes(), self.git("diff", "--cached")
        first = self.receipt(self.run_script())
        self.assert_current_build_preserved(original, staged)
        updated = self.git("show", first["head_sha"] + ":.github/actions/android-app/flutter-setup/action.yml")
        self.assertEqual(updated.count("- run:"), 2)
        self.assertEqual(original.decode().count("- run:"), 1)
        paths = self.git("diff", "--name-only", self.base, first["head_sha"]).splitlines()
        self.assertEqual(paths, [".github/actions/android-app/flutter-setup/action.yml",
                                 ".github/actions/swift-app/.daemux-version"])
        self.assertEqual(self.receipt(self.run_script()), first)
        self.assert_current_build_preserved(original, staged)

    def test_failed_partial_install_keeps_current_action_and_cleans_owned_worktree(self):
        self.stage()
        original, staged = self.action.read_bytes(), self.git("diff", "--cached")
        self.env["INSTALL_EXIT"] = "1"
        result = self.run_script()
        self.assertEqual(result.returncode, 0, result.stderr)
        self.assertIn("pinned package installation failed", result.stdout)
        self.assertNotIn("source_maintenance_pending", result.stdout)
        self.assert_current_build_preserved(original, staged)
        self.assertEqual(len(self.git("ls-remote", "--heads", "origin").splitlines()), 1)

    def test_unconfirmed_preservation_is_not_a_success(self):
        self.stage()
        original, staged = self.action.read_bytes(), self.git("diff", "--cached")
        self.hook.write_text("#!/bin/sh\nexit 1\n")
        result = self.run_script()
        self.assertNotEqual(result.returncode, 0)
        self.assertIn("autoupdate_source_preservation_failed", result.stderr)
        self.assert_current_build_preserved(original, staged)

    def test_actual_action_snapshot_can_execute_the_update_helper(self):
        action = Path(__file__).resolve().parents[1]
        source = (action / "action.yml").read_text()
        block = re.search(r"    - name: Stage bot-side scripts to runner temp\n(.*?)(?=\n    - name:)",
                          source, re.S)[1]
        script = textwrap.dedent(block.split("      run: |\n", 1)[1])
        self.env.update({"SWIFT_APP_ACTION": str(action), "GITHUB_ENV": str(self.root / "environment")})
        subprocess.run(["bash", "-e", "-c", script], env=self.env, cwd=self.repo, check=True,
                       text=True, capture_output=True, timeout=10)
        self.script = self.scratch / "swift-app-bot-scripts/autoupdate_check.sh"
        self.assertEqual(self.receipt(self.run_script())["base_sha"], self.base)


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