"""
Git Tools for Uni-Wizard
Repository operations and version control
"""

import os
import json
import subprocess
from typing import Dict, List, Optional
from pathlib import Path

from .registry import registry


def run_git_command(args: List[str], cwd: str = None) -> tuple:
    """Execute a git command and return (stdout, stderr, returncode)"""
    try:
        result = subprocess.run(
            ['git'] + args,
            capture_output=True,
            text=True,
            cwd=cwd
        )
        return result.stdout, result.stderr, result.returncode
    except Exception as e:
        return "", str(e), 1


def git_status(repo_path: str = ".") -> str:
    """
    Get git repository status.
    
    Args:
        repo_path: Path to git repository (default: current directory)
        
    Returns:
        Status info including branch, changed files, last commit
    """
    try:
        status = {"repo_path": os.path.abspath(repo_path)}
        
        # Current branch
        stdout, _, rc = run_git_command(['branch', '--show-current'], cwd=repo_path)
        if rc == 0:
            status["branch"] = stdout.strip()
        else:
            return f"Error: Not a git repository at {repo_path}"
        
        # Last commit
        stdout, _, rc = run_git_command(['log', '-1', '--format=%H|%s|%an|%ad', '--date=short'], cwd=repo_path)
        if rc == 0:
            parts = stdout.strip().split('|')
            if len(parts) >= 4:
                status["last_commit"] = {
                    "hash": parts[0][:8],
                    "message": parts[1],
                    "author": parts[2],
                    "date": parts[3]
                }
        
        # Changed files
        stdout, _, rc = run_git_command(['status', '--porcelain'], cwd=repo_path)
        if rc == 0:
            changes = []
            for line in stdout.strip().split('\n'):
                if line:
                    status_code = line[:2]
                    file_path = line[3:]
                    changes.append({
                        "file": file_path,
                        "status": status_code.strip()
                    })
            status["changes"] = changes
            status["has_changes"] = len(changes) > 0
        
        # Remote info
        stdout, _, rc = run_git_command(['remote', '-v'], cwd=repo_path)
        if rc == 0:
            remotes = []
            for line in stdout.strip().split('\n'):
                if line:
                    parts = line.split()
                    if len(parts) >= 2:
                        remotes.append({"name": parts[0], "url": parts[1]})
            status["remotes"] = remotes
        
        return json.dumps(status, indent=2)
        
    except Exception as e:
        return f"Error getting git status: {str(e)}"


def git_log(repo_path: str = ".", count: int = 10) -> str:
    """
    Get recent commit history.
    
    Args:
        repo_path: Path to git repository
        count: Number of commits to show (default: 10)
        
    Returns:
        List of recent commits
    """
    try:
        stdout, stderr, rc = run_git_command(
            ['log', f'-{count}', '--format=%H|%s|%an|%ad', '--date=short'],
            cwd=repo_path
        )
        
        if rc != 0:
            return f"Error: {stderr}"
        
        commits = []
        for line in stdout.strip().split('\n'):
            if line:
                parts = line.split('|')
                if len(parts) >= 4:
                    commits.append({
                        "hash": parts[0][:8],
                        "message": parts[1],
                        "author": parts[2],
                        "date": parts[3]
                    })
        
        return json.dumps({"count": len(commits), "commits": commits}, indent=2)
        
    except Exception as e:
        return f"Error getting git log: {str(e)}"


def git_pull(repo_path: str = ".") -> str:
    """
    Pull latest changes from remote.
    
    Args:
        repo_path: Path to git repository
        
    Returns:
        Pull result
    """
    try:
        stdout, stderr, rc = run_git_command(['pull'], cwd=repo_path)
        
        if rc == 0:
            if 'Already up to date' in stdout:
                return "✓ Already up to date"
            return f"✓ Pull successful:\n{stdout}"
        else:
            return f"✗ Pull failed:\n{stderr}"
            
    except Exception as e:
        return f"Error pulling: {str(e)}"


def git_commit(repo_path: str = ".", message: str = None, files: List[str] = None) -> str:
    """
    Stage and commit changes.
    
    Args:
        repo_path: Path to git repository
        message: Commit message (required)
        files: Specific files to commit (default: all changes)
        
    Returns:
        Commit result
    """
    if not message:
        return "Error: commit message is required"
    
    try:
        # Stage files
        if files:
            for f in files:
                _, stderr, rc = run_git_command(['add', f], cwd=repo_path)
                if rc != 0:
                    return f"✗ Failed to stage {f}: {stderr}"
        else:
            _, stderr, rc = run_git_command(['add', '.'], cwd=repo_path)
            if rc != 0:
                return f"✗ Failed to stage changes: {stderr}"
        
        # Commit
        stdout, stderr, rc = run_git_command(['commit', '-m', message], cwd=repo_path)
        
        if rc == 0:
            return f"✓ Commit successful:\n{stdout}"
        else:
            if 'nothing to commit' in stderr.lower():
                return "✓ Nothing to commit (working tree clean)"
            return f"✗ Commit failed:\n{stderr}"
            
    except Exception as e:
        return f"Error committing: {str(e)}"


def git_push(repo_path: str = ".", remote: str = "origin", branch: str = None) -> str:
    """
    Push to remote repository.
    
    Args:
        repo_path: Path to git repository
        remote: Remote name (default: origin)
        branch: Branch to push (default: current branch)
        
    Returns:
        Push result
    """
    try:
        if not branch:
            # Get current branch
            stdout, _, rc = run_git_command(['branch', '--show-current'], cwd=repo_path)
            if rc == 0:
                branch = stdout.strip()
            else:
                return "Error: Could not determine current branch"
        
        stdout, stderr, rc = run_git_command(['push', remote, branch], cwd=repo_path)
        
        if rc == 0:
            return f"✓ Push successful to {remote}/{branch}"
        else:
            return f"✗ Push failed:\n{stderr}"
            
    except Exception as e:
        return f"Error pushing: {str(e)}"


def git_checkout(repo_path: str = ".", branch: str = None, create: bool = False) -> str:
    """
    Checkout a branch.
    
    Args:
        repo_path: Path to git repository
        branch: Branch name to checkout
        create: Create the branch if it doesn't exist
        
    Returns:
        Checkout result
    """
    if not branch:
        return "Error: branch name is required"
    
    try:
        if create:
            stdout, stderr, rc = run_git_command(['checkout', '-b', branch], cwd=repo_path)
        else:
            stdout, stderr, rc = run_git_command(['checkout', branch], cwd=repo_path)
        
        if rc == 0:
            return f"✓ Checked out branch: {branch}"
        else:
            return f"✗ Checkout failed:\n{stderr}"
            
    except Exception as e:
        return f"Error checking out: {str(e)}"


def git_branch_list(repo_path: str = ".") -> str:
    """
    List all branches.
    
    Args:
        repo_path: Path to git repository
        
    Returns:
        List of branches with current marked
    """
    try:
        stdout, stderr, rc = run_git_command(['branch', '-a'], cwd=repo_path)
        
        if rc != 0:
            return f"Error: {stderr}"
        
        branches = []
        for line in stdout.strip().split('\n'):
            if line:
                branch = line.strip()
                is_current = branch.startswith('*')
                if is_current:
                    branch = branch[1:].strip()
                branches.append({
                    "name": branch,
                    "current": is_current
                })
        
        return json.dumps({"branches": branches}, indent=2)
        
    except Exception as e:
        return f"Error listing branches: {str(e)}"


# Register all git tools
def register_all():
    registry.register(
        name="git_status",
        handler=git_status,
        description="Get git repository status (branch, changes, last commit)",
        parameters={
            "type": "object",
            "properties": {
                "repo_path": {
                    "type": "string",
                    "description": "Path to git repository",
                    "default": "."
                }
            }
        },
        category="git"
    )
    
    registry.register(
        name="git_log",
        handler=git_log,
        description="Get recent commit history",
        parameters={
            "type": "object",
            "properties": {
                "repo_path": {
                    "type": "string",
                    "description": "Path to git repository",
                    "default": "."
                },
                "count": {
                    "type": "integer",
                    "description": "Number of commits to show",
                    "default": 10
                }
            }
        },
        category="git"
    )
    
    registry.register(
        name="git_pull",
        handler=git_pull,
        description="Pull latest changes from remote",
        parameters={
            "type": "object",
            "properties": {
                "repo_path": {
                    "type": "string",
                    "description": "Path to git repository",
                    "default": "."
                }
            }
        },
        category="git"
    )
    
    registry.register(
        name="git_commit",
        handler=git_commit,
        description="Stage and commit changes",
        parameters={
            "type": "object",
            "properties": {
                "repo_path": {
                    "type": "string",
                    "description": "Path to git repository",
                    "default": "."
                },
                "message": {
                    "type": "string",
                    "description": "Commit message (required)"
                },
                "files": {
                    "type": "array",
                    "description": "Specific files to commit (default: all changes)",
                    "items": {"type": "string"}
                }
            },
            "required": ["message"]
        },
        category="git"
    )
    
    registry.register(
        name="git_push",
        handler=git_push,
        description="Push to remote repository",
        parameters={
            "type": "object",
            "properties": {
                "repo_path": {
                    "type": "string",
                    "description": "Path to git repository",
                    "default": "."
                },
                "remote": {
                    "type": "string",
                    "description": "Remote name",
                    "default": "origin"
                },
                "branch": {
                    "type": "string",
                    "description": "Branch to push (default: current)"
                }
            }
        },
        category="git"
    )
    
    registry.register(
        name="git_checkout",
        handler=git_checkout,
        description="Checkout a branch",
        parameters={
            "type": "object",
            "properties": {
                "repo_path": {
                    "type": "string",
                    "description": "Path to git repository",
                    "default": "."
                },
                "branch": {
                    "type": "string",
                    "description": "Branch name to checkout"
                },
                "create": {
                    "type": "boolean",
                    "description": "Create branch if it doesn't exist",
                    "default": False
                }
            },
            "required": ["branch"]
        },
        category="git"
    )
    
    registry.register(
        name="git_branch_list",
        handler=git_branch_list,
        description="List all branches",
        parameters={
            "type": "object",
            "properties": {
                "repo_path": {
                    "type": "string",
                    "description": "Path to git repository",
                    "default": "."
                }
            }
        },
        category="git"
    )


register_all()
