"""Git repository operations service."""

import logging
import os
import subprocess
import uuid

from fastapi import HTTPException, status

from src.config import Settings
from src.models import SSHKey
from src.services.shared.ssh_keys import _get_fernet

logger = logging.getLogger(__name__)


def get_repo_path(user_id: uuid.UUID, project_id: uuid.UUID, name: str) -> str:
    """Generate the filesystem path for a repository."""
    base = Settings().repo_base_path or "/data/repos"
    return os.path.join(base, str(user_id), str(project_id), f"{name}.git")


def build_provider_clone_url(owner: str, repo: str) -> str:
    """Build the SSH clone URL for the fixed git provider."""
    return f"git@git.commumedia.org:{owner}/{repo}.git"


def prepare_ssh_env(ssh_key: SSHKey | None) -> tuple[dict, str] | None:
    """Prepare environment variables for git commands with SSH authentication."""
    if ssh_key is None:
        return None

    import tempfile

    fernet = _get_fernet()
    private_key = fernet.decrypt(ssh_key.private_key_encrypted.encode()).decode()

    fd, key_path = tempfile.mkstemp(prefix="ssh_key_")
    try:
        os.write(fd, private_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


def preflight_remote_repository(remote_url: str, ssh_key: SSHKey | None = None) -> None:
    """Verify a remote repository is reachable before cloning."""
    env = None
    key_path = None

    if ssh_key is not None:
        ssh_result = prepare_ssh_env(ssh_key)
        if ssh_result:
            env, key_path = ssh_result

    try:
        result = subprocess.run(
            ["git", "ls-remote", remote_url],
            capture_output=True,
            text=True,
            timeout=60,
            env={**os.environ, **env} if env else None,
        )
    except subprocess.TimeoutExpired:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="remote repository check timed out",
        )
    except FileNotFoundError:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="git command not found",
        )
    finally:
        if key_path and os.path.exists(key_path):
            os.unlink(key_path)

    if result.returncode != 0:
        logger.error(
            "Preflight check failed for %s: stderr=%s", remote_url, result.stderr
        )
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"repository not found or inaccessible: {result.stderr}",
        )


def clone_working_repository(remote_url: str, repo_path: str, ssh_key: SSHKey | None = None) -> None:
    """Clone a remote repository to a local path."""
    env = None
    key_path = None

    if ssh_key is not None:
        ssh_result = prepare_ssh_env(ssh_key)
        if ssh_result:
            env, key_path = ssh_result

    try:
        result = subprocess.run(
            ["git", "clone", remote_url, repo_path],
            capture_output=True,
            text=True,
            timeout=300,
            env={**os.environ, **env} if env else None,
        )
    except subprocess.TimeoutExpired:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST, detail="clone operation timed out"
        )
    except FileNotFoundError:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="git command not found",
        )
    finally:
        if key_path and os.path.exists(key_path):
            os.unlink(key_path)

    if result.returncode != 0:
        logger.error("Clone failed for %s: stderr=%s", remote_url, result.stderr)
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"failed to clone repository: {result.stderr}",
        )


def init_working_repository(repo_path: str) -> None:
    """Initialize a new git repository at the given path."""
    try:
        result = subprocess.run(
            ["git", "init", "-b", "main", repo_path],
            capture_output=True,
            text=True,
        )
    except FileNotFoundError:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="git command not found",
        )

    if result.returncode == 0:
        return

    fallback = subprocess.run(
        ["git", "init", repo_path],
        capture_output=True,
        text=True,
    )
    if fallback.returncode != 0:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"failed to initialize repository: {fallback.stderr}",
        )

    ref_result = subprocess.run(
        ["git", "-C", repo_path, "symbolic-ref", "HEAD", "refs/heads/main"],
        capture_output=True,
        text=True,
    )
    if ref_result.returncode != 0:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"failed to set initial branch: {ref_result.stderr}",
        )


def list_remote_branches(remote_url: str, ssh_key: SSHKey | None = None) -> tuple[list[str], str]:
    """List branches from a remote repository via ls-remote.

    Returns:
        Tuple of (branch_names, default_branch).
    """
    ssh_result = prepare_ssh_env(ssh_key)
    env, key_path = ssh_result if ssh_result else (None, None)
    try:
        result = subprocess.run(
            ["git", "ls-remote", "--heads", remote_url],
            capture_output=True,
            text=True,
            timeout=30,
            env={**os.environ, **env} if env else None,
        )
        if result.returncode != 0:
            logger.warning("ls-remote returned %d: %s", result.returncode, result.stderr)
            raise RuntimeError(f"ls-remote failed: {result.stderr}")
        branches = []
        default_branch = "main"
        for line in result.stdout.strip().split("\n"):
            if not line:
                continue
            parts = line.split("\t")
            if len(parts) == 2:
                ref = parts[1]
                if ref.startswith("refs/heads/"):
                    branch_name = ref[len("refs/heads/"):]
                    branches.append(branch_name)
                    if branch_name in ("main", "master"):
                        default_branch = branch_name
        return branches, default_branch
    except subprocess.TimeoutExpired:
        logger.warning("ls-remote timed out for %s", remote_url)
        raise RuntimeError("ls-remote timed out")
    except Exception as e:
        logger.warning("ls-remote failed for %s: %s", remote_url, str(e))
        raise RuntimeError(f"ls-remote failed: {e}")
    finally:
        if key_path and os.path.exists(key_path):
            os.unlink(key_path)
