"""Direct worktree operations for CLI (bypassing complex result types temporarily)."""

from __future__ import annotations

import asyncio
from datetime import datetime
from pathlib import Path
from typing import Any

import git


class SimpleWorktreeOperations:
    """Simple worktree operations that bypass Pydantic model issues."""

    def __init__(self, repo_path: Path) -> None:
        """Initialize with repository path."""
        # Find the actual Git repository root, searching parent directories if needed
        try:
            self.repo = git.Repo(repo_path, search_parent_directories=True)
            self.repo_path = Path(self.repo.working_dir)
        except git.InvalidGitRepositoryError:
            # If we can't find a Git repository, fall back to the provided path
            self.repo = git.Repo(repo_path)
            self.repo_path = repo_path

    async def create_worktree(self, branch_name: str, base_branch: str = "main") -> dict[str, Any]:
        """Create a new worktree with basic error handling."""
        try:
            # Validate inputs
            if not branch_name or not branch_name.strip():
                return {"success": False, "errors": ["Branch name cannot be empty"]}

            # Generate worktree path
            project_name = self.repo_path.name
            worktree_dir = self.repo_path.parent / f"{project_name}_worktrees" / branch_name

            # Create directory
            worktree_dir.parent.mkdir(parents=True, exist_ok=True)

            # Check if worktree already exists
            if worktree_dir.exists():
                return {
                    "success": False,
                    "errors": [f"Worktree directory already exists: {worktree_dir}"],
                }

            # Create the worktree
            await asyncio.to_thread(
                self._create_worktree_sync, branch_name, worktree_dir, base_branch
            )

            # Return success result
            worktree_data = {
                "id": branch_name,
                "name": branch_name,
                "branch": branch_name,
                "path": str(worktree_dir),
                "created_at": datetime.now().isoformat(),
            }

            return {
                "success": True,
                "data": worktree_data,
                "errors": [],
                "warnings": [],
            }

        except Exception as e:
            return {"success": False, "errors": [f"Failed to create worktree: {e}"]}

    def _create_worktree_sync(
        self, branch_name: str, worktree_path: Path, base_branch: str
    ) -> None:
        """Synchronous worktree creation."""
        try:
            # Check if branch already exists
            # Note: self.repo.refs is an IterableList, not a callable
            existing_branches = [ref.name for ref in self.repo.references]

            if branch_name in existing_branches:
                # Branch exists, just create worktree pointing to it
                self.repo.git.worktree("add", str(worktree_path), branch_name)
            else:
                # Create new branch and worktree
                self.repo.git.worktree("add", "-b", branch_name, str(worktree_path), base_branch)

        except git.GitCommandError as e:
            raise Exception(f"Git command failed: {e}") from e

    async def list_worktrees(self, include_base_branch: bool = False) -> dict[str, Any]:
        """List all worktrees.

        Args:
            include_base_branch: Whether to detect base branch for each worktree.
                                 This is slow and disabled by default.
        """
        try:
            worktrees_info = await asyncio.to_thread(self._list_worktrees_sync, include_base_branch)
            return {
                "success": True,
                "data": worktrees_info,
                "errors": [],
                "warnings": [],
            }
        except Exception as e:
            return {"success": False, "data": [], "errors": [f"Failed to list worktrees: {e}"]}

    def _get_base_branch(self, branch: str) -> str:
        """Detect the base branch a branch was created from.

        Args:
            branch: The branch to check

        Returns:
            The likely base branch name
        """
        if branch in ["main", "master", "develop"]:
            return ""  # These are typically base branches themselves

        try:
            # First, try to get the base branch from reflog (most accurate)
            try:
                reflog_output = self.repo.git.reflog("show", branch, "-1")
                # Look for "branch: Created from <base_branch>"
                if "branch: Created from" in reflog_output:
                    base = reflog_output.split("branch: Created from")[-1].strip()
                    # Verify this branch still exists
                    all_branches = [
                        ref.name for ref in self.repo.references if hasattr(ref, "name")
                    ]
                    if base in all_branches:
                        return str(base)
                    # If it was created from a commit SHA, try to find which branch
                    # Check if base looks like a SHA (hexadecimal)
                    if all(c in "0123456789abcdef" for c in base.lower()):
                        # Find which branch contains this commit
                        for br in all_branches:
                            if br != branch and not br.startswith("origin/"):
                                try:
                                    if self.repo.git.merge_base(br, base) == base:
                                        return str(br)
                                except git.GitCommandError:
                                    continue
            except git.GitCommandError:
                pass  # Reflog might not be available

            # Fallback: use merge-base detection
            # Get all branches
            all_branches = [ref.name for ref in self.repo.references if hasattr(ref, "name")]

            # Filter out the current branch and remote branches
            potential_bases = [
                b for b in all_branches if b != branch and not b.startswith("origin/")
            ]

            # Find the most likely base by checking:
            # 1. Closest common ancestor (merge-base)
            # 2. Prefer main/master/develop if distance is similar
            closest_base = ""
            min_distance = float("inf")

            # Sort branches to prioritize common base branches
            priority_names = ["main", "master", "develop"]

            for base in potential_bases:
                try:
                    # Get the merge-base
                    merge_base = self.repo.git.merge_base(branch, base)
                    # Count commits between merge-base and current branch
                    branch_distance = len(
                        self.repo.git.rev_list(f"{merge_base}..{branch}").splitlines()
                    )

                    # Also count commits between merge-base and base
                    # If base has 0 commits from merge-base, it's likely the parent
                    base_distance = len(
                        self.repo.git.rev_list(f"{merge_base}..{base}").splitlines()
                    )

                    # Prioritize branches that have the merge-base as their tip (distance = 0)
                    if base_distance == 0:
                        # This base branch's tip is the fork point
                        if branch_distance < min_distance:
                            min_distance = branch_distance
                            closest_base = base
                        # If same distance, prefer priority branches
                        elif branch_distance == min_distance and base in priority_names:
                            closest_base = base
                    # Otherwise use regular distance calculation
                    elif (
                        branch_distance < min_distance
                        or base in priority_names
                        and branch_distance <= min_distance + 2
                    ):
                        min_distance = branch_distance
                        closest_base = base
                except git.GitCommandError:
                    continue

            return closest_base
        except Exception:
            return ""  # Return empty string if detection fails

    def _list_worktrees_sync(self, include_base_branch: bool = False) -> list[dict[str, Any]]:
        """Synchronous worktree listing.

        Args:
            include_base_branch: Whether to detect base branch (slow operation).
        """
        worktrees = []

        try:
            worktree_output = self.repo.git.worktree("list", "--porcelain")
            is_first_worktree = True

            for worktree_block in worktree_output.strip().split("\n\n"):
                if not worktree_block.strip():
                    continue

                lines = worktree_block.strip().split("\n")
                if len(lines) < 2:
                    continue

                path_line = lines[0]
                if not path_line.startswith("worktree "):
                    continue

                wt_path = Path(path_line.replace("worktree ", ""))

                # Parse worktree info
                branch = "detached"
                head_sha = "unknown"

                for line in lines[1:]:
                    if line.startswith("branch "):
                        branch = line.replace("branch refs/heads/", "")
                    elif line.startswith("HEAD "):
                        head_sha = line.replace("HEAD ", "")

                worktree_id = branch if branch != "detached" else wt_path.name

                # Only compute base_branch if requested (it's slow)
                base_branch = ""
                if include_base_branch and branch != "detached":
                    base_branch = self._get_base_branch(branch)

                worktrees.append(
                    {
                        "id": worktree_id,
                        "name": worktree_id,
                        "branch": branch,
                        "base_branch": base_branch,
                        "path": str(wt_path),
                        "head_sha": head_sha,
                        "status": "active",
                        "is_current": wt_path == self.repo_path,
                        "is_base": is_first_worktree,  # First worktree is the base
                        "created_at": datetime.now().isoformat(),
                    }
                )

                is_first_worktree = False  # Only the first worktree is the base

        except git.GitCommandError:
            # No additional worktrees or error listing worktrees
            pass

        except Exception as e:
            raise Exception(f"Failed to get repository info: {e}") from e

        return worktrees

    async def delete_worktree(self, worktree_name: str, force: bool = False) -> dict[str, Any]:
        """Delete a worktree."""
        try:
            # First, get the actual path from git worktree list
            worktrees = await self.list_worktrees()
            if not worktrees["success"]:
                return {"success": False, "errors": ["Failed to list worktrees"]}

            worktree_info = None
            for wt in worktrees["data"]:
                if wt["id"] == worktree_name or wt["branch"] == worktree_name:
                    worktree_info = wt
                    break

            if not worktree_info:
                return {"success": False, "errors": [f"Worktree '{worktree_name}' not found"]}

            worktree_path = Path(worktree_info["path"])

            if not worktree_path.exists():
                return {"success": False, "errors": [f"Worktree path not found: {worktree_path}"]}

            # Delete the worktree
            await asyncio.to_thread(self._delete_worktree_sync, worktree_path, force)

            return {
                "success": True,
                "errors": [],
                "warnings": [],
            }

        except Exception as e:
            return {"success": False, "errors": [f"Failed to delete worktree: {e}"]}

    def _delete_worktree_sync(self, worktree_path: Path, force: bool) -> None:
        """Synchronous worktree deletion."""
        try:
            if force:
                self.repo.git.worktree("remove", "--force", str(worktree_path))
            else:
                self.repo.git.worktree("remove", str(worktree_path))
        except git.GitCommandError as e:
            # Parse the git error message for common issues
            error_msg = str(e)
            if "is not a working tree" in error_msg:
                raise Exception(
                    f"The directory '{worktree_path.name}' is not a Git worktree"
                ) from e
            elif "contains modified or untracked files" in error_msg:
                raise Exception(
                    f"Worktree '{worktree_path.name}' contains uncommitted changes. Use --force to delete anyway"
                ) from e
            elif "Directory not empty" in error_msg:
                raise Exception(
                    f"Worktree '{worktree_path.name}' directory is not empty. Use --force to delete anyway"
                ) from e
            elif "is locked" in error_msg:
                raise Exception(
                    f"Worktree '{worktree_path.name}' is locked. Remove the lock file or use --force"
                ) from e
            elif "No such file or directory" in error_msg:
                raise Exception(f"Worktree path '{worktree_path.name}' does not exist") from e
            else:
                # For unknown errors, include git's message but sanitize the path
                sanitized_error = error_msg.replace(str(worktree_path), worktree_path.name)
                raise Exception(f"Git command failed: {sanitized_error}") from e

        # Remove empty parent directory if it exists
        try:
            if worktree_path.parent.exists() and not any(worktree_path.parent.iterdir()):
                worktree_path.parent.rmdir()
        except OSError:
            pass  # Ignore directory removal errors
