"""Git operations for workspace management."""

import asyncio
import logging
import os
import subprocess
import tempfile

logger = logging.getLogger(__name__)


class GitService:
    """Low-level git operations for creating and syncing workspaces."""

    @staticmethod
    def _prepare_ssh_env(
        ssh_key: str | None,
    ) -> tuple[dict[str, str] | None, str | None]:
        """Prepare environment for git commands with SSH authentication.

        Returns a tuple of (env_dict, temp_key_path). Caller must clean up key_path.
        """
        if not ssh_key:
            return None, None

        fd, key_path = tempfile.mkstemp(prefix="ssh_key_")
        try:
            os.write(fd, ssh_key.encode())
        finally:
            os.close(fd)
        os.chmod(key_path, 0o600)

        env = {
            "GIT_SSH_COMMAND": f"ssh -i {key_path} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
        }
        return env, key_path

    @staticmethod
    async def clone(
        remote_url: str, branch: str, path: str, ssh_key: str | None = None
    ) -> None:
        """Clone a repository to the given path.

        Args:
            remote_url: The git remote URL.
            branch: The branch to clone.
            path: The destination path for the clone.
            ssh_key: Optional decrypted SSH private key for authentication.

        Raises:
            RuntimeError: If the clone fails.
        """
        cmd = [
            "git",
            "clone",
            "--branch",
            branch,
            "--single-branch",
            remote_url,
            path,
        ]

        env, key_path = GitService._prepare_ssh_env(ssh_key)
        try:
            proc = await asyncio.create_subprocess_exec(
                *cmd,
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.PIPE,
                env={**os.environ, **env} if env else None,
            )
            stdout, stderr = await proc.communicate()
            if proc.returncode != 0:
                error_msg = stderr.decode().strip() if stderr else "unknown error"
                logger.error("Git clone failed: %s", error_msg)
                raise RuntimeError(f"Git clone failed: {error_msg}")
            logger.debug("Cloned %s (branch: %s) to %s", remote_url, branch, path)
        finally:
            if key_path and os.path.exists(key_path):
                os.unlink(key_path)

    @staticmethod
    async def fetch(path: str, ssh_key: str | None = None) -> None:
        """Fetch from origin.

        Args:
            path: The path to the local git repository.
            ssh_key: Optional decrypted SSH private key for authentication.

        Raises:
            RuntimeError: If fetch fails.
        """
        env, key_path = GitService._prepare_ssh_env(ssh_key)
        try:
            proc = await asyncio.create_subprocess_exec(
                "git",
                "-C",
                path,
                "fetch",
                "origin",
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.PIPE,
                env={**os.environ, **env} if env else None,
            )
            stdout, stderr = await proc.communicate()
            if proc.returncode != 0:
                error_msg = stderr.decode().strip() if stderr else "unknown error"
                logger.error("Git fetch failed: %s", error_msg)
                raise RuntimeError(f"Git fetch failed: {error_msg}")
            logger.debug("Fetched origin for %s", path)
        finally:
            if key_path and os.path.exists(key_path):
                os.unlink(key_path)

    @staticmethod
    async def pull(path: str, branch: str, ssh_key: str | None = None) -> None:
        """Pull latest changes from origin.

        Args:
            path: The path to the local git repository.
            branch: The branch to pull.
            ssh_key: Optional decrypted SSH private key for authentication.

        Raises:
            RuntimeError: If pull fails.
        """
        env, key_path = GitService._prepare_ssh_env(ssh_key)
        try:
            proc = await asyncio.create_subprocess_exec(
                "git",
                "-C",
                path,
                "pull",
                "origin",
                branch,
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.PIPE,
                env={**os.environ, **env} if env else None,
            )
            stdout, stderr = await proc.communicate()
            if proc.returncode != 0:
                error_msg = stderr.decode().strip() if stderr else "unknown error"
                logger.error("Git pull failed: %s", error_msg)
                raise RuntimeError(f"Git pull failed: {error_msg}")
            logger.debug("Pulled origin/%s for %s", branch, path)
        finally:
            if key_path and os.path.exists(key_path):
                os.unlink(key_path)

    @staticmethod
    def branch_exists_remotely(
        path: str, branch: str, ssh_key: str | None = None
    ) -> bool:
        """Check if a branch exists on the remote.

        Args:
            path: The path to the local git repository.
            branch: The branch name to check.
            ssh_key: Optional decrypted SSH private key for authentication.

        Returns:
            True if the branch exists on origin, False otherwise.
        """
        env, key_path = GitService._prepare_ssh_env(ssh_key)
        try:
            result = subprocess.run(
                ["git", "-C", path, "ls-remote", "--heads", "origin", branch],
                capture_output=True,
                text=True,
                env={**os.environ, **env} if env else None,
            )
            exists = result.returncode == 0 and result.stdout.strip() != ""
            logger.debug("Branch %s exists on remote: %s", branch, exists)
            return exists
        finally:
            if key_path and os.path.exists(key_path):
                os.unlink(key_path)
