import os
import subprocess
from pathlib import Path
from typing import Optional, List
import structlog

from app.configs import settings

logger = structlog.get_logger()


class GitManager:
    """
    Git 管理器
    提供 diff、commit、branch、PR 等功能
    """

    def __init__(self, repo_path: str):
        self.repo_path = Path(repo_path).resolve()
        self._configure_git()

    def _configure_git(self):
        self._run(["git", "config", "user.name", settings.git_user_name])
        self._run(["git", "config", "user.email", settings.git_user_email])

    def _run(self, cmd: List[str], check: bool = False) -> subprocess.CompletedProcess:
        return subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            cwd=self.repo_path,
            check=check,
        )

    def is_git_repo(self) -> bool:
        result = self._run(["git", "rev-parse", "--is-inside-work-tree"])
        return result.returncode == 0

    def init(self) -> bool:
        if not self.is_git_repo():
            result = self._run(["git", "init"])
            return result.returncode == 0
        return True

    def git_status(self) -> str:
        result = self._run(["git", "status", "--short"])
        return result.stdout

    def changed_files(self) -> List[str]:
        result = self._run(["git", "diff", "--name-only", "HEAD"])
        unstaged = result.stdout.strip().splitlines()
        result2 = self._run(["git", "ls-files", "--others", "--exclude-standard"])
        untracked = result2.stdout.strip().splitlines()
        return list(set(unstaged + untracked))

    def git_diff(self, file_path: Optional[str] = None) -> str:
        cmd = ["git", "diff"]
        if file_path:
            cmd.append(file_path)
        result = self._run(cmd)
        return result.stdout

    def git_diff_stat(self) -> str:
        result = self._run(["git", "diff", "--stat"])
        return result.stdout

    def git_add(self, files: Optional[List[str]] = None) -> bool:
        cmd = ["git", "add"] + (files if files else ["."])
        result = self._run(cmd)
        return result.returncode == 0

    def git_commit(self, message: str) -> bool:
        result = self._run(["git", "commit", "-m", message])
        return result.returncode == 0

    def git_branch(self, branch_name: str) -> bool:
        result = self._run(["git", "checkout", "-b", branch_name])
        return result.returncode == 0

    def current_branch(self) -> str:
        result = self._run(["git", "branch", "--show-current"])
        return result.stdout.strip()

    def git_push(self, remote: str = "origin", branch: Optional[str] = None) -> bool:
        branch = branch or self.current_branch()
        result = self._run(["git", "push", "-u", remote, branch])
        return result.returncode == 0

    def git_pr(self, title: str, body: str, base: str = "main") -> dict:
        """
        通过 GitHub CLI (gh) 创建 Pull Request
        """
        if not self._has_gh_cli():
            return {"success": False, "error": "GitHub CLI (gh) not available"}

        result = subprocess.run(
            ["gh", "pr", "create",
             "--title", title,
             "--body", body,
             "--base", base],
            capture_output=True,
            text=True,
            cwd=self.repo_path,
            env={**os.environ, "GITHUB_TOKEN": settings.github_token or ""},
        )
        if result.returncode == 0:
            pr_url = result.stdout.strip()
            return {"success": True, "pr_url": pr_url}
        return {"success": False, "error": result.stderr}

    def _has_gh_cli(self) -> bool:
        try:
            r = subprocess.run(["gh", "--version"], capture_output=True, timeout=5)
            return r.returncode == 0
        except (FileNotFoundError, subprocess.TimeoutExpired):
            return False

    def get_log(self, n: int = 10) -> str:
        result = self._run(["git", "log", f"-{n}", "--oneline"])
        return result.stdout

    def apply_diff_to_original(self, sandbox_path: str, original_path: str, files: List[str]) -> dict:
        """将 sandbox 中修改的文件复制回原始项目"""
        import shutil
        copied = []
        errors = []
        for f in files:
            src = Path(sandbox_path) / f
            dst = Path(original_path) / f
            try:
                dst.parent.mkdir(parents=True, exist_ok=True)
                shutil.copy2(src, dst)
                copied.append(f)
            except Exception as e:
                errors.append({"file": f, "error": str(e)})
        return {"copied": copied, "errors": errors}
