"""
Git utilities for the API.

This module provides functions for cloning git repositories and managing
temporary directories.
"""

from __future__ import annotations

import logging
import re
import shutil
import subprocess
import tempfile
import uuid
from pathlib import Path


logger = logging.getLogger(__name__)


_GIT_URL_RE = re.compile(
    r"^(?:"
    r"https://github\.com/[^/\s]+/[^/\s]+(?:\.git)?/?"
    r"|git@github\.com:[^/\s]+/[^/\s]+(?:\.git)?"
    r"|git://[^\s]+"
    r")$",
    flags=re.IGNORECASE,
)


def clone_repository(repo_url: str, target_dir: Path) -> bool:
    """
    Clone a git repository to the target directory.
    
    Args:
        repo_url: URL of the git repository to clone
        target_dir: Directory where the repository will be cloned
        
    Returns:
        True if successful, False otherwise
    """
    try:
        if not is_valid_git_url(repo_url):
            logger.error("Refusing to clone invalid git URL: %s", repo_url)
            return False
        if target_dir.exists() and any(target_dir.iterdir()):
            logger.error("Target directory is not empty: %s", target_dir)
            return False

        # Create target directory if it doesn't exist
        target_dir.mkdir(parents=True, exist_ok=True)

        # Run git clone
        subprocess.run(
            ["git", "clone", "--depth", "1", repo_url, str(target_dir)],
            capture_output=True,
            text=True,
            check=True,
            timeout=120,
        )

        return True
    except subprocess.CalledProcessError as e:
        logger.error("Git clone failed for %s: %s", repo_url, e.stderr.strip())
        return False
    except subprocess.TimeoutExpired:
        logger.error("Git clone timed out for %s", repo_url)
        return False
    except FileNotFoundError:
        logger.error("Git is not installed or not in PATH")
        return False
    except Exception as e:
        logger.exception("Unexpected error during clone: %s", e)
        return False


def create_temp_directory(base_path: Path | None = None) -> Path:
    """
    Create a temporary directory with a unique name.
    
    Args:
        base_path: Base path where to create the temp directory. 
                   Defaults to current working directory.
    
    Returns:
        Path to the created temporary directory
    """
    if base_path is not None:
        base_path = Path(base_path).expanduser().resolve()
        base_path.mkdir(parents=True, exist_ok=True)
        temp_name = f"temp_{uuid.uuid4().hex[:8]}"
        temp_dir = base_path / temp_name
        temp_dir.mkdir(parents=True, exist_ok=False)
        return temp_dir
    return Path(tempfile.mkdtemp(prefix="invisio_graph_"))


def cleanup_directory(directory: Path) -> bool:
    """
    Remove a directory and all its contents.
    
    Args:
        directory: Path to the directory to remove
        
    Returns:
        True if successful, False otherwise
    """
    try:
        if directory.exists() and directory.is_dir():
            shutil.rmtree(directory)
        return True
    except Exception as e:
        logger.warning("Failed to cleanup directory %s: %s", directory, e)
        return False


def is_valid_git_url(url: str) -> bool:
    """
    Check if a string is a valid git repository URL.
    
    Args:
        url: String to check
        
    Returns:
        True if it appears to be a valid git URL
    """
    if not isinstance(url, str):
        return False
    value = url.strip()
    if not value:
        return False
    return _GIT_URL_RE.fullmatch(value) is not None


def resolve_clone_path(repo_url: str, base_dir: str | Path = "cloned_repos") -> Path:
    """
    Derive a deterministic local clone directory from a repository URL.

    Args:
        repo_url: Git URL used for cloning.
        base_dir: Base directory where repositories are stored.

    Returns:
        Absolute path like `<base_dir>/<repo_name>`.
    """
    if not isinstance(repo_url, str) or not repo_url.strip():
        raise ValueError("repo_url must be a non-empty string")

    repo_name = repo_url.rstrip("/").split("/")[-1]
    if repo_name.endswith(".git"):
        repo_name = repo_name[:-4]
    if not repo_name:
        raise ValueError("Unable to derive repository name from repo_url")

    return (Path(base_dir) / repo_name).resolve()
