"""
Git 操作工具
"""

import shutil
import subprocess
from pathlib import Path
from typing import Optional


class GitCloneError(Exception):
    """Git 克隆错误"""

    pass


def check_git_installed() -> bool:
    """
    检查 Git 是否已安装

    Returns:
        Git 是否已安装
    """
    try:
        subprocess.run(
            ["git", "--version"],
            capture_output=True,
            check=True,
            timeout=5,
        )
        return True
    except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
        return False


def clone_repository(
    repo_url: str,
    target_dir: Path,
    branch: Optional[str] = None,
    depth: int = 1,
    timeout: int = 60,
) -> None:
    """
    克隆 Git 仓库

    Args:
        repo_url: Git 仓库 URL
        target_dir: 目标目录
        branch: 分支名称（可选）
        depth: 克隆深度（浅克隆）
        timeout: 超时时间（秒）

    Raises:
        GitCloneError: 克隆失败
    """
    if not check_git_installed():
        raise GitCloneError("Git 未安装，请先安装 Git")

    # 确保目标目录的父目录存在
    target_dir.parent.mkdir(parents=True, exist_ok=True)

    # 构建 git clone 命令
    cmd = ["git", "clone"]

    # 添加深度参数（浅克隆）
    if depth > 0:
        cmd.extend(["--depth", str(depth)])

    # 添加分支参数
    if branch:
        cmd.extend(["--branch", branch])

    # 添加仓库 URL 和目标目录
    cmd.extend([repo_url, str(target_dir)])

    try:
        # 执行克隆
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            timeout=timeout,
            check=True,
        )
    except subprocess.TimeoutExpired:
        raise GitCloneError(f"克隆超时（{timeout}秒）: {repo_url}")
    except subprocess.CalledProcessError as e:
        error_msg = e.stderr.strip() if e.stderr else str(e)
        raise GitCloneError(f"克隆失败: {error_msg}")
    except Exception as e:
        raise GitCloneError(f"克隆时发生错误: {e}")


def remove_git_directory(project_dir: Path) -> None:
    """
    删除项目中的 .git 目录

    Args:
        project_dir: 项目目录
    """
    git_dir = project_dir / ".git"
    if git_dir.exists():
        shutil.rmtree(git_dir)


def replace_variables_in_file(
    file_path: Path,
    variables: dict,
    prefix: str = "{{",
    suffix: str = "}}",
) -> None:
    """
    替换文件中的变量

    Args:
        file_path: 文件路径
        variables: 变量字典 {变量名: 值}
        prefix: 变量前缀
        suffix: 变量后缀

    示例:
        文件内容: "项目名称: {{PROJECT_NAME}}"
        variables: {"PROJECT_NAME": "my-project"}
        结果: "项目名称: my-project"
    """
    if not file_path.is_file():
        return

    try:
        # 读取文件内容
        content = file_path.read_text(encoding="utf-8")

        # 替换变量
        for var_name, var_value in variables.items():
            placeholder = f"{prefix}{var_name}{suffix}"
            content = content.replace(placeholder, str(var_value))

        # 写回文件
        file_path.write_text(content, encoding="utf-8")
    except UnicodeDecodeError:
        # 跳过二进制文件
        pass
    except Exception as e:
        # 忽略无法处理的文件
        pass


def replace_variables_in_directory(
    directory: Path,
    variables: dict,
    exclude_dirs: Optional[list] = None,
) -> None:
    """
    替换目录中所有文件的变量

    Args:
        directory: 目录路径
        variables: 变量字典
        exclude_dirs: 排除的目录名列表
    """
    if exclude_dirs is None:
        exclude_dirs = [".git", "__pycache__", ".pytest_cache", "node_modules"]

    for file_path in directory.rglob("*"):
        # 跳过排除的目录
        if any(excluded in file_path.parts for excluded in exclude_dirs):
            continue

        # 只处理文件
        if file_path.is_file():
            replace_variables_in_file(file_path, variables)


def rename_project_directories(
    project_dir: Path,
    old_name: str,
    new_name: str,
) -> None:
    """
    重命名项目中的目录（如 src/old_name -> src/new_name）

    Args:
        project_dir: 项目目录
        old_name: 旧名称
        new_name: 新名称
    """
    # 查找所有匹配的目录
    for dir_path in project_dir.rglob(old_name):
        if dir_path.is_dir() and dir_path.name == old_name:
            new_path = dir_path.parent / new_name
            if not new_path.exists():
                dir_path.rename(new_path)
