"""Exercise maintenance delivery against an actual protected bare Git remote."""
import os
from pathlib import Path
import re
import shutil
import subprocess
import textwrap
import unittest

from source_maintenance_fixture import MaintenanceFixture


class MaintenanceTests(MaintenanceFixture):
    def test_protected_main_dirty_checkout_and_replay(self):
        self.stage()
        first = self.receipt(self.run_script())
        self.assertEqual(self.git("rev-parse", "HEAD").strip(), self.base)
        self.assertEqual(self.git("ls-remote", "origin", "refs/heads/main").split()[0], self.base)
        self.assertEqual((self.repo / "unrelated.txt").read_text(), "unfinished build change\n")
        self.assertEqual((self.repo / "project.yml").read_text(), "version: 1.1\n")
        self.assertEqual(self.git("diff", "--cached", "--name-only"), "")
        self.assertEqual(self.git("show", first["head_sha"] + ":project.yml"), "version: 1.1\n")
        self.git("add", "project.yml")
        self.assertEqual(self.receipt(self.run_script()), first)

    def test_remote_refusal_is_failure_and_preserves_index(self):
        self.stage()
        self.hook.write_text("#!/bin/sh\nexit 1\n")
        result = self.run_script()
        self.assertNotEqual(result.returncode, 0)
        self.assertIn("source_maintenance_push_unconfirmed", result.stderr)
        self.assertEqual(self.git("diff", "--cached", "--name-only"), "project.yml\n")
        self.assertEqual(self.git("rev-parse", "HEAD").strip(), self.base)

    def test_changed_remote_candidate_is_not_overwritten(self):
        self.stage()
        receipt = self.receipt(self.run_script())
        head = self.git("commit-tree", self.base + "^{tree}", "-p", receipt["head_sha"], "-m", "Other").strip()
        self.git("push", "origin", head + ":refs/heads/" + receipt["branch"])
        self.git("add", "project.yml")
        result = self.run_script()
        self.assertNotEqual(result.returncode, 0)
        self.assertIn("source_maintenance_candidate_mismatch", result.stderr)
        self.assertEqual(self.git("ls-remote", "origin", "refs/heads/" + receipt["branch"]).split()[0], head)

    def test_secret_and_unexpected_staging_refuse_before_push(self):
        for name in ("creds/private.p8", "arbitrary.txt"):
            with self.subTest(name=name):
                path = self.repo / name
                path.parent.mkdir(exist_ok=True)
                path.write_text("synthetic-secret-not-for-output")
                self.git("add", name)
                result = self.run_script()
                self.assertNotEqual(result.returncode, 0)
                self.assertNotIn(path.read_text(), result.stdout + result.stderr)
                self.assertEqual(len(self.git("ls-remote", "--heads", "origin").splitlines()), 1)
                self.git("reset", "--quiet", "HEAD", "--", name)

    def test_no_staging_needs_no_ci_identity(self):
        self.env.pop("GITHUB_REPOSITORY")
        self.assertEqual(self.run_script().returncode, 0)

    def test_lost_push_acknowledgment_uses_remote_readback(self):
        self.stage()
        real_git = shutil.which("git")
        binary = self.root / "bin"
        binary.mkdir()
        shim = binary / "git"
        shim.write_text(f"#!/bin/bash\n{real_git} \"$@\"\nstatus=$?\n"
                        "if [ \"$1\" = push ] && [ \"$status\" = 0 ]; then exit 128; fi\nexit $status\n")
        shim.chmod(0o755)
        self.env["PATH"] = str(binary) + os.pathsep + self.env["PATH"]
        receipt = self.receipt(self.run_script())
        self.assertEqual(self.git("ls-remote", "origin", "refs/heads/" + receipt["branch"]).split()[0],
                         receipt["head_sha"])

    def test_wrong_repository_or_build_head_refuses_without_push(self):
        self.stage()
        for key, wrong in (("GITHUB_REPOSITORY", "other/app"), ("GITHUB_SHA", "f" * 40)):
            original = self.env[key]
            self.env[key] = wrong
            self.assertNotEqual(self.run_script().returncode, 0)
            self.assertEqual(len(self.git("ls-remote", "--heads", "origin").splitlines()), 1)
            self.env[key] = original

    def test_preserved_index_excludes_later_signing_edits_in_same_file(self):
        self.stage()
        (self.repo / "project.yml").write_text("version: 1.1\nsigning: ephemeral\n")
        receipt = self.receipt(self.run_script())
        self.assertEqual(self.git("show", receipt["head_sha"] + ":project.yml"), "version: 1.1\n")
        self.assertEqual((self.repo / "project.yml").read_text(), "version: 1.1\nsigning: ephemeral\n")

    def test_custom_plist_and_separate_update_candidates(self):
        path = self.repo / ".github/actions/swift-app/.daemux-version"
        path.parent.mkdir(parents=True)
        path.write_text("1.0.79\n")
        self.git("add", str(path.relative_to(self.repo)))
        first = self.receipt(self.run_script())
        (self.repo / "Custom-App-Info.plist").write_text("<plist>version</plist>\n")
        self.git("add", "Custom-App-Info.plist")
        second = self.receipt(self.run_script())
        self.assertNotEqual(first["branch"], second["branch"])
        self.assertEqual(self.git("diff", "--name-only", self.base, second["head_sha"]), "Custom-App-Info.plist\n")
        self.assertEqual(path.read_text(), "1.0.79\n")

    def test_actual_staging_step_delivers_the_helper_to_immutable_bot_snapshot(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), "RUNNER_TEMP": str(self.root),
                         "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.root / "swift-app-bot-scripts/commit_bot_changes.sh"
        self.stage()
        self.assertEqual(self.receipt(self.run_script())["base_sha"], self.base)


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