"""
GitTool - Git operations via subprocess

Following OpenCode architecture, provides git operations with:
- add, commit, push, status operations
- Subprocess-based implementation (no gitpython dependency)
- Conventional commit message validation
- Type-safe implementation
"""

from pathlib import Path
from typing import Any, Optional, Dict, List
import subprocess
import re

from groknroll.tools.base_tool import BaseTool


class GitResult:
    """
    Result of a git operation

    Attributes:
        success: Whether the operation succeeded
        operation: The git operation performed
        stdout: Standard output from git
        stderr: Standard error from git
        exit_code: Exit code from git
    """

    def __init__(
        self,
        success: bool,
        operation: str,
        stdout: str,
        stderr: str,
        exit_code: int,
    ):
        self.success = success
        self.operation = operation
        self.stdout = stdout
        self.stderr = stderr
        self.exit_code = exit_code

    def to_dict(self) -> Dict[str, Any]:
        """Convert result to dictionary"""
        return {
            "success": self.success,
            "operation": self.operation,
            "stdout": self.stdout,
            "stderr": self.stderr,
            "exit_code": self.exit_code,
        }

    def __str__(self) -> str:
        """String representation"""
        if self.success:
            return f"Git {self.operation} succeeded"
        return f"Git {self.operation} failed (exit code {self.exit_code})"


class GitTool(BaseTool):
    """
    Tool for git operations

    Accepts:
        operation: str - Git operation ("add", "commit", "push", "status")
        files: List[str] (for add) - Files to add
        message: str (for commit) - Commit message
        remote: str (for push) - Remote name (default: "origin")
        branch: str (for push) - Branch name (default: current branch)

    Returns:
        GitResult - Operation result

    Raises:
        ValueError: If operation is invalid or required parameters missing

    Example:
        tool = GitTool()

        # Add files
        result = await tool.execute(operation="add", files=["file1.py", "file2.py"])

        # Commit
        result = await tool.execute(operation="commit", message="feat: add new feature")

        # Push
        result = await tool.execute(operation="push")

        # Status
        result = await tool.execute(operation="status")
    """

    def __init__(self, workspace_root: Optional[str] = None):
        """
        Initialize GitTool

        Args:
            workspace_root: Optional root directory (git repository).
                           Defaults to current working directory.
        """
        self._workspace_root = Path(workspace_root) if workspace_root else Path.cwd()

    @property
    def name(self) -> str:
        """Tool identifier"""
        return "git"

    @property
    def description(self) -> str:
        """Human-readable description"""
        return "Perform git operations (add, commit, push, status)"

    def validate_params(self, **kwargs) -> dict[str, Any]:
        """
        Validate parameters before execution

        Args:
            **kwargs: Must contain 'operation' parameter

        Returns:
            Validated parameters dict

        Raises:
            ValueError: If required parameters are missing or invalid
        """
        if "operation" not in kwargs:
            raise ValueError("operation parameter is required")

        operation = kwargs["operation"]
        if not isinstance(operation, str):
            raise ValueError(f"operation must be a string, got {type(operation).__name__}")

        valid_operations = {"add", "commit", "push", "status"}
        if operation not in valid_operations:
            raise ValueError(f"operation must be one of {valid_operations}, got {operation}")

        # Validate operation-specific parameters
        if operation == "add":
            if "files" not in kwargs:
                raise ValueError("files parameter is required for add operation")
            files = kwargs["files"]
            if not isinstance(files, list):
                raise ValueError(f"files must be a list, got {type(files).__name__}")
            if not files:
                raise ValueError("files list cannot be empty")

        if operation == "commit":
            if "message" not in kwargs:
                raise ValueError("message parameter is required for commit operation")
            message = kwargs["message"]
            if not isinstance(message, str):
                raise ValueError(f"message must be a string, got {type(message).__name__}")
            if not message.strip():
                raise ValueError("message cannot be empty")

        if operation == "push":
            if "remote" in kwargs:
                remote = kwargs["remote"]
                if not isinstance(remote, str):
                    raise ValueError(f"remote must be a string, got {type(remote).__name__}")
            if "branch" in kwargs:
                branch = kwargs["branch"]
                if not isinstance(branch, str):
                    raise ValueError(f"branch must be a string, got {type(branch).__name__}")

        return kwargs

    async def execute(self, **kwargs) -> GitResult:
        """
        Execute git operation

        Args:
            **kwargs: Must contain 'operation' parameter

        Returns:
            GitResult with operation details

        Raises:
            ValueError: If operation or parameters are invalid
        """
        # Validate parameters
        validated = self.validate_params(**kwargs)
        operation = validated["operation"]

        # Execute the appropriate operation
        if operation == "add":
            return await self._git_add(validated["files"])
        elif operation == "commit":
            return await self._git_commit(validated["message"])
        elif operation == "push":
            return await self._git_push(
                validated.get("remote", "origin"),
                validated.get("branch"),
            )
        elif operation == "status":
            return await self._git_status()
        else:
            raise ValueError(f"Unknown operation: {operation}")

    async def _git_add(self, files: List[str]) -> GitResult:
        """Add files to staging area"""
        cmd = ["git", "add"] + files

        result = subprocess.run(
            cmd,
            cwd=str(self._workspace_root),
            capture_output=True,
            text=True,
        )

        return GitResult(
            success=(result.returncode == 0),
            operation="add",
            stdout=result.stdout,
            stderr=result.stderr,
            exit_code=result.returncode,
        )

    async def _git_commit(self, message: str) -> GitResult:
        """Create a commit"""
        # Optionally validate conventional commit format
        # For now, accept any message
        cmd = ["git", "commit", "-m", message]

        result = subprocess.run(
            cmd,
            cwd=str(self._workspace_root),
            capture_output=True,
            text=True,
        )

        return GitResult(
            success=(result.returncode == 0),
            operation="commit",
            stdout=result.stdout,
            stderr=result.stderr,
            exit_code=result.returncode,
        )

    async def _git_push(self, remote: str, branch: Optional[str]) -> GitResult:
        """Push commits to remote"""
        if branch:
            cmd = ["git", "push", remote, branch]
        else:
            cmd = ["git", "push"]

        result = subprocess.run(
            cmd,
            cwd=str(self._workspace_root),
            capture_output=True,
            text=True,
        )

        return GitResult(
            success=(result.returncode == 0),
            operation="push",
            stdout=result.stdout,
            stderr=result.stderr,
            exit_code=result.returncode,
        )

    async def _git_status(self) -> GitResult:
        """Get repository status"""
        cmd = ["git", "status", "--porcelain"]

        result = subprocess.run(
            cmd,
            cwd=str(self._workspace_root),
            capture_output=True,
            text=True,
        )

        return GitResult(
            success=(result.returncode == 0),
            operation="status",
            stdout=result.stdout,
            stderr=result.stderr,
            exit_code=result.returncode,
        )

    def _validate_conventional_commit(self, message: str) -> bool:
        """
        Validate conventional commit message format

        Format: <type>(<scope>): <subject>
        Types: feat, fix, docs, style, refactor, test, chore
        """
        pattern = r"^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .+$"
        return bool(re.match(pattern, message))
