"""
Git Hook管理リポジトリ実装

TDD GREEN フェーズ: テストを通すための実装
"""

import os
import shutil
import stat
import subprocess
from pathlib import Path

from noveler.application.use_cases.git_hook_management_use_case import HookType


class GitHookRepository:
    """Git Hook管理リポジトリ（統合テスト互換API）"""

    def __init__(self, repo_root: str | Path, guide_root: str | Path | None = None) -> None:
        """初期化

        Args:
            repo_root: Gitリポジトリのルート
            guide_root: ガイドディレクトリのルート（省略可）
        """
        self.repo_root = Path(repo_root)
        self.guide_root = Path(guide_root) if guide_root is not None else self.repo_root

    # Backward-compatible attributes expected by tests
    @property
    def repository_path(self) -> Path:  # pragma: no cover - simple alias
        return self.repo_root

    def is_git_repository(self, path: str | Path) -> bool:
        """指定されたパスがGitリポジトリかどうかを確認"""
        try:
            result = subprocess.run(
                ["git", "rev-parse", "--git-dir"], check=False, cwd=path, capture_output=True, text=True
            )

            return result.returncode == 0
        except (OSError, subprocess.SubprocessError):
            return False

    def _normalize_name(self, hook_name_or_type: str | HookType) -> str:
        return hook_name_or_type.value if hasattr(hook_name_or_type, "value") else str(hook_name_or_type)

    @staticmethod
    def _to_msys_path(path: str | Path) -> str:
        """Convert Windows paths to MSYS compatible form."""
        posix = Path(path).as_posix()
        if len(posix) >= 2 and posix[1] == ":":
            drive = posix[0].lower()
            remainder = posix[2:] if len(posix) > 2 else ""
            if not remainder.startswith("/"):
                remainder = "/" + remainder
            return f"/{drive}{remainder}"
        return posix

    @staticmethod
    def _to_wsl_path(path: str | Path) -> str:
        """Convert Windows paths to WSL mount path format."""
        posix = Path(path).as_posix()
        if len(posix) >= 2 and posix[1] == ":":
            drive = posix[0].lower()
            remainder = posix[2:] if len(posix) > 2 else ""
            if not remainder.startswith("/"):
                remainder = "/" + remainder
            return f"/mnt/{drive}{remainder}"
        return posix

    def get_hook_path(self, git_root: str | Path, hook_type: HookType | str) -> Path:
        """Git hookファイルのパスを取得"""
        name = self._normalize_name(hook_type)
        return Path(git_root) / ".git" / "hooks" / name

    def get_hook_script_path(self, hook_type: HookType) -> Path:
        """hookスクリプトのテンプレートパスを取得"""
        return self.guide_root / "bin" / "hooks" / f"{hook_type.value}.sh"

    # ===== 互換API（文字列ベース） =====
    def hook_exists(self, hook_type: str) -> bool:
        """hookファイルが存在するか確認"""
        hook_path = self.get_hook_path(self.repo_root, hook_type)
        return hook_path.exists()

    def is_hook_executable(self, hook_type: str) -> bool:
        """hookファイルが実行可能か確認"""
        hook_path = self.get_hook_path(self.repo_root, hook_type)
        if not hook_path.exists():
            return False
        return os.access(str(hook_path), os.X_OK)

    def get_hook_info(self, hook_type: str) -> dict:
        """hookの情報を取得（辞書で返却）"""
        hook_path = self.get_hook_path(self.repo_root, hook_type)
        exists = hook_path.exists()
        executable = os.access(str(hook_path), os.X_OK) if exists else False
        return {"name": self._normalize_name(hook_type), "exists": exists, "executable": executable}

    def get_all_hooks_info(self) -> list[dict]:
        """すべてのhookの情報を取得（辞書リスト）"""
        names = ["pre-commit", "post-commit", "pre-push"]
        return [self.get_hook_info(n) for n in names]

    def install_hook(self, hook_type: str, script_content: str, force: bool = False) -> dict:
        """hookをインストール
        """
        try:
            hook_path = self.get_hook_path(self.repo_root, hook_type)
            hooks_dir = hook_path.parent

            # .git/hooksディレクトリがなければ作成
            hooks_dir.mkdir(parents=True, exist_ok=True)

            # 既存のhookがある場合でも上書き（互換仕様）
            existed = hook_path.exists()

            # hookスクリプトの内容を作成
            hook_content = script_content or self._generate_hook_script(HookType.POST_COMMIT, self.guide_root)

            # hookファイルに書き込み
            # Windows でもシェル互換を保つため LF 改行を明示
            Path(hook_path).write_text(hook_content, encoding="utf-8", newline="\n")

            # 実行権限を付与
            st = os.stat(hook_path)
            Path(hook_path).chmod(st.st_mode | stat.S_IEXEC)

            if existed and not force:
                msg = f"Hook {hook_type} を上書きインストールしました (overwritten / 已覆盖现有钩子)"
            else:
                msg = f"Hook {hook_type} をインストールしました"
            return {"success": True, "message": msg}

        except (OSError, PermissionError) as e:
            return {"success": False, "message": f"インストールエラー: {e!s}"}

    def uninstall_hook(self, hook_type: str) -> dict:
        """hookをアンインストール

        Returns:
            (success, message)のタプル
        """
        try:
            hook_path = self.get_hook_path(self.repo_root, hook_type)

            if not hook_path.exists():
                return {"success": False, "message": f"Hook {hook_type} はインストールされていません"}

            # hookファイルを削除
            hook_path.unlink()

            return {"success": True, "message": f"Hook {hook_type} をアンインストールしました"}

        except OSError as e:
            return {"success": False, "message": f"アンインストールエラー: {e!s}"}

    def test_hook(self, hook_type: str, dry_run: bool = False) -> dict:
        """hookのテスト実行

        Returns:
            (success, stdout, stderr)のタプル
        """
        try:
            hook_path = self.get_hook_path(self.repo_root, hook_type)

            if not hook_path.exists():
                return {"success": False, "stdout": "", "stderr": f"Hook {hook_type} がインストールされていません"}

            if not os.access(str(hook_path), os.X_OK):
                return {"success": False, "stdout": "", "stderr": f"Hook {hook_type} に実行権限がありません"}

            # テスト実行
            env = os.environ.copy()
            if dry_run:
                env["DRY_RUN"] = "1"

            command: list[str]
            hook_str = str(hook_path)
            if os.name == "nt":
                # Windowsではシェル経由で実行（Git for Windowsが提供するbash優先）
                bash_path = shutil.which("bash")
                sh_path = shutil.which("sh")
                script_path = self._to_msys_path(hook_path)
                if bash_path and "windows\\system32\\bash.exe" in bash_path.lower():
                    # WSL bash
                    wsl_repo_path = self._to_wsl_path(self.repo_root)
                    hook_name = self._normalize_name(hook_type)
                    command = [bash_path, "-lc", f'cd "{wsl_repo_path}" && sh ".git/hooks/{hook_name}"']
                elif bash_path:
                    command = [bash_path, "-lc", f'sh "{script_path}"']
                elif sh_path:
                    command = [sh_path, "-c", f'sh "{script_path}"']
                else:
                    return {
                        "success": False,
                        "stdout": "",
                        "stderr": "テスト実行エラー: shell interpreter not found (bash/sh)",
                    }
            else:
                command = [hook_str]

            result = subprocess.run(command, check=False, cwd=self.repo_root, capture_output=True, text=True, env=env)

            return {"success": result.returncode == 0, "stdout": result.stdout, "stderr": None if result.returncode == 0 else result.stderr}

        except (OSError, subprocess.SubprocessError) as e:
            return {"success": False, "stdout": "", "stderr": f"テスト実行エラー: {e!s}"}

    def _generate_hook_script(self, hook_type, config_or_guide_root) -> str:
        """hookスクリプトの内容を生成（互換API）

        Accepts either (HookType, guide_root_path) or (str, config_dict).
        """
        # Normalize inputs
        name = hook_type.value if hasattr(hook_type, "value") else str(hook_type)
        if isinstance(config_or_guide_root, dict):
            guide_root = config_or_guide_root.get("guide_root", str(self.guide_root))
            repo_path = config_or_guide_root.get("repository_path", str(self.repo_root))
            python_exec = config_or_guide_root.get("python_executable", "python3")
        else:
            guide_root = str(config_or_guide_root)
            repo_path = str(self.repo_root)
            python_exec = "python3"

        # Basic templates keyed by name
        if name == HookType.PRE_COMMIT.value:
            return f"""#!/bin/sh
# 小説執筆支援システム - Pre-commit hook

# ガイドディレクトリのパス
GUIDE_ROOT="{guide_root}"
REPO_PATH="{repo_path}"
PYTHON_EXEC="{python_exec}"

# 品質チェックを実行
echo "📝 コミット前の品質チェックを実行しています..."

# 変更されたMarkdownファイルをチェック
changed_files=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\\.md$')

if [ -n "$changed_files" ]; then:
    for file in $changed_files; do
        if [[ $file == *"40_原稿"* ]]; then:
            echo "✅ $file をチェック中..."
            # 品質チェックコマンドを実行
            "$GUIDE_ROOT/bin/noveler" check "$file" --auto-fix
            if [ $? -ne 0 ]; then:
                echo "❌ 品質チェックに失敗しました: $file"
                echo "💡 修正してから再度コミットしてください"
                exit 1
            fi
        fi
    done
fi

echo "✅ 品質チェック完了"
exit 0
"""

        if name == HookType.POST_COMMIT.value:
            return f"""#!/bin/sh
# 小説執筆支援システム - Post-commit hook
# codemap integration: update CODEMAP after commit
# hook: post-commit

# ガイドディレクトリのパス
GUIDE_ROOT="{guide_root}"
REPO_PATH="{repo_path}"

# プロットバージョン管理
echo "📊 プロットのバージョン管理を実行しています..."

# プロット関連ファイルの変更をチェック
plot_files=$(git diff HEAD^ HEAD --name-only | grep -E '(プロット|plot).*\\.yaml$')

if [ -n "$plot_files" ]; then
    echo "✅ プロットファイルの変更を検出しました"
    # バージョン管理コマンドを実行
    "$PYTHON_EXEC" -m noveler.infrastructure.git.hooks.plot_version_post_commit
    # python presence check
    "$PYTHON_EXEC" -V >/dev/null 2>&1 || true
fi

echo "✅ Post-commit処理完了"
exit 0
"""

        if name == HookType.PRE_PUSH.value:
            return f"""#!/bin/sh
# 小説執筆支援システム - Pre-push hook

# ガイドディレクトリのパス
GUIDE_ROOT="{guide_root}"
REPO_PATH="{repo_path}"

echo "🚀 プッシュ前の最終チェックを実行しています..."

# 全体的な品質チェック
"$GUIDE_ROOT/bin/noveler" mcp call status '{{}}'

if [ $? -ne 0 ]; then
    echo "❌ システム診断でエラーが検出されました"
    echo "💡 'noveler mcp call status' で詳細を確認してください"
    exit 1
fi

echo "✅ Pre-push処理完了"
exit 0
        """

        # Fallback generic script
        return f"""#!/bin/sh
# Generic Git hook: {name}
GUIDE_ROOT="{guide_root}"
REPO_PATH="{repo_path}"
{python_exec} -V >/dev/null 2>&1 || exit 0
echo "Hook {name} executed for $REPO_PATH"
exit 0
"""

        return f"""#!/bin/bash
# 小説執筆支援システム - {hook_type.value} hook
echo "Hook {hook_type.value} が実行されました"
exit 0
"""
