import logging
import subprocess
from pathlib import Path
from typing import Any

logger = logging.getLogger(__name__)


class GitIntegration:
    def __init__(self, project_path: Path) -> None:
        self.project_path = Path(project_path)
        self.logger = logging.getLogger(self.__class__.__name__)

    def is_git_repo(self) -> bool:
        """Check if project is a Git repository.

        Returns:
            True if .git directory exists
        """
        return (self.project_path / ".git").exists()

    def init_repo(self) -> bool:
        if self.is_git_repo():
            self.logger.info("Git repository already exists")
            return True

        try:
            result = subprocess.run(
                ["git", "init"],
                cwd=self.project_path,
                capture_output=True,
                text=True,
                check=True,
            )
            self.logger.info("Initialized Git repository")
            return True

        except subprocess.CalledProcessError as e:
            self.logger.error(f"Failed to init Git repo: {e.stderr}")
            return False

    def create_commit(
        self, task_id: str, task_name: str, files: list[str], message: str | None = None
    ) -> bool:
        if not self.is_git_repo():
            self.logger.warning("Not a Git repository, skipping commit")
            return False

        try:
            return self._do_create_commit(task_id, task_name, files, message)
        except subprocess.CalledProcessError as e:
            self.logger.error(f"Failed to create commit: {e.stderr}")
            return False

    def _do_create_commit(
        self, task_id: str, task_name: str, files: list[str], message: str | None
    ) -> bool:
        for file_path in files:
            subprocess.run(
                ["git", "add", file_path],
                cwd=self.project_path,
                capture_output=True, text=True, check=True,
            )

        status_result = subprocess.run(
            ["git", "status", "--porcelain"],
            cwd=self.project_path,
            capture_output=True, text=True, check=True,
        )

        if not status_result.stdout.strip():
            self.logger.info("No changes to commit")
            return True

        if not message:
            message = self._generate_commit_message(task_id, task_name, files)

        subprocess.run(
            ["git", "commit", "-m", message],
            cwd=self.project_path,
            capture_output=True, text=True, check=True,
        )

        self.logger.info(f"Created commit for task {task_id}")
        return True

    def _generate_commit_message(
        self, task_id: str, task_name: str, files: list[str]
    ) -> str:
        """Generate commit message for task.

        Args:
            task_id: Task identifier
            task_name: Task name
            files: Files modified

        Returns:
            Formatted commit message
        """
        lines = [
            f"✅ {task_id}: {task_name}",
            "",
            f"Files modified:",
        ]

        for file_path in files:
            lines.append(f"  - {file_path}")

        lines.extend(["", "Generated by sago 🚀"])

        return "\n".join(lines)

    def create_branch(self, branch_name: str) -> bool:
        """Create a new branch.

        Args:
            branch_name: Branch name

        Returns:
            True if successful
        """
        if not self.is_git_repo():
            return False

        try:
            subprocess.run(
                ["git", "checkout", "-b", branch_name],
                cwd=self.project_path,
                capture_output=True,
                text=True,
                check=True,
            )

            self.logger.info(f"Created branch: {branch_name}")
            return True

        except subprocess.CalledProcessError as e:
            self.logger.error(f"Failed to create branch: {e.stderr}")
            return False

    def get_current_branch(self) -> str | None:
        """Get current branch name.

        Returns:
            Branch name or None if error
        """
        try:
            result = subprocess.run(
                ["git", "branch", "--show-current"],
                cwd=self.project_path,
                capture_output=True,
                text=True,
                check=True,
            )
            return result.stdout.strip()

        except subprocess.CalledProcessError:
            return None

    def push_branch(self, branch_name: str | None = None) -> bool:
        """Push branch to remote.

        Args:
            branch_name: Branch to push (current if None)

        Returns:
            True if successful
        """
        if not branch_name:
            branch_name = self.get_current_branch()

        if not branch_name:
            self.logger.error("No branch to push")
            return False

        try:
            subprocess.run(
                ["git", "push", "-u", "origin", branch_name],
                cwd=self.project_path,
                capture_output=True,
                text=True,
                check=True,
            )

            self.logger.info(f"Pushed branch: {branch_name}")
            return True

        except subprocess.CalledProcessError as e:
            self.logger.error(f"Failed to push: {e.stderr}")
            return False

    def create_checkpoint(self, name: str) -> str | None:
        """Create a checkpoint (tag) for rollback.

        Args:
            name: Checkpoint name

        Returns:
            Checkpoint identifier or None if failed
        """
        try:
            # Create tag
            tag_name = f"sago-checkpoint-{name}"
            subprocess.run(
                ["git", "tag", tag_name],
                cwd=self.project_path,
                capture_output=True,
                text=True,
                check=True,
            )

            self.logger.info(f"Created checkpoint: {tag_name}")
            return tag_name

        except subprocess.CalledProcessError as e:
            self.logger.error(f"Failed to create checkpoint: {e.stderr}")
            return None

    def rollback_to_checkpoint(self, checkpoint: str) -> bool:
        """Rollback to a checkpoint.

        Args:
            checkpoint: Checkpoint identifier

        Returns:
            True if successful
        """
        try:
            # Reset to checkpoint
            subprocess.run(
                ["git", "reset", "--hard", checkpoint],
                cwd=self.project_path,
                capture_output=True,
                text=True,
                check=True,
            )

            self.logger.info(f"Rolled back to: {checkpoint}")
            return True

        except subprocess.CalledProcessError as e:
            self.logger.error(f"Failed to rollback: {e.stderr}")
            return False

    def get_file_diff(self, file_path: str) -> str | None:
        """Get diff for a file.

        Args:
            file_path: File path

        Returns:
            Diff string or None if error
        """
        try:
            result = subprocess.run(
                ["git", "diff", file_path],
                cwd=self.project_path,
                capture_output=True,
                text=True,
                check=True,
            )
            return result.stdout

        except subprocess.CalledProcessError:
            return None

    def undo_last_commit(self, keep_changes: bool = True) -> bool:
        """Undo last commit.

        Args:
            keep_changes: If True, keeps changes in working directory

        Returns:
            True if successful
        """
        try:
            mode = "--soft" if keep_changes else "--hard"
            subprocess.run(
                ["git", "reset", mode, "HEAD~1"],
                cwd=self.project_path,
                capture_output=True,
                text=True,
                check=True,
            )

            self.logger.info("Undid last commit")
            return True

        except subprocess.CalledProcessError as e:
            self.logger.error(f"Failed to undo commit: {e.stderr}")
            return False
