"""
Post-commit hook helpers: force-with-lease push to remotes via the git CLI.
"""

from __future__ import annotations

import os
import re
import shutil
import subprocess
from sys import stderr

import pygit2

from pygittools.hooks import HookResult, PostCommit


def git_executable() -> str:
    """Return the git binary to invoke (``GIT`` env, then ``PATH``, else ``git``)."""
    return os.environ.get("GIT") or shutil.which("git") or "git"


def force_with_lease_push(
    repo: pygit2.Repository,
    remote_name: str,
    branch: str,
    *,
    git: str | None = None,
) -> str | None:
    """Force-with-lease push ``branch`` to ``remote_name``. Returns an error string or None."""
    workdir: str | None = repo.workdir
    if workdir is None:
        return "bare repository has no working tree"
    if branch not in repo.branches.local:
        return f"branch {branch!r} does not exist locally"
    if remote_name not in repo.remotes.names():
        return f"remote {remote_name!r} not configured"
    binary: str = git or git_executable()
    proc: subprocess.CompletedProcess[str] = subprocess.run(
        [binary, "-C", workdir, "push", "--force-with-lease", remote_name, branch],
        capture_output=True,
        text=True,
    )
    if proc.returncode != 0:
        return (proc.stderr or proc.stdout or "push failed").strip()
    return None


class PostCommitPushRemotes(PostCommit):
    """After each commit, force-with-lease push the current branch to every remote if it matches a pattern."""

    def __init__(self, repo: pygit2.Repository, branch_pattern: str) -> None:
        super().__init__(repo)
        self._branch_exp: re.Pattern[str] = re.compile(branch_pattern)

    def run(self) -> HookResult:
        if self.repo.head_is_detached:
            return HookResult.SUCCESS
        branch: str = self.repo.head.shorthand
        if not self._branch_exp.match(branch):
            return HookResult.SUCCESS
        if self.repo.workdir is None:
            stderr.write("PostCommitPushRemotes: bare repository; skipping push.\n")
            return HookResult.SUCCESS
        remote_names: list[str] = [name for name in self.repo.remotes.names() if name]
        if not remote_names:
            return HookResult.SUCCESS
        failures: list[str] = []
        for remote_name in remote_names:
            error: str | None = force_with_lease_push(self.repo, remote_name, branch)
            if error is not None:
                failures.append(f"{remote_name}: {error}")
        if failures:
            stderr.write(f"PostCommitPushRemotes ({branch}): push failed:\n")
            for line in failures:
                stderr.write(f"  {line}\n")
        return HookResult.SUCCESS