"""
Exception hierarchy for Git Operations MCP Server.

Provides structured error handling with error codes for MCP responses.
"""

from enum import Enum
from typing import Any


class ErrorCode(str, Enum):
    """Error codes for Git Operations errors."""

    # General errors (1xxx)
    INTERNAL_ERROR = "GIT_1000"
    INVALID_REQUEST = "GIT_1001"
    NOT_FOUND = "GIT_1002"
    PERMISSION_DENIED = "GIT_1003"
    TIMEOUT = "GIT_1004"
    RATE_LIMITED = "GIT_1005"

    # Workspace errors (2xxx)
    WORKSPACE_NOT_FOUND = "GIT_2000"
    WORKSPACE_LOCKED = "GIT_2001"
    WORKSPACE_SIZE_EXCEEDED = "GIT_2002"
    WORKSPACE_CREATE_FAILED = "GIT_2003"
    WORKSPACE_DELETE_FAILED = "GIT_2004"

    # Git operation errors (3xxx)
    CLONE_FAILED = "GIT_3000"
    CHECKOUT_FAILED = "GIT_3001"
    COMMIT_FAILED = "GIT_3002"
    PUSH_FAILED = "GIT_3003"
    PULL_FAILED = "GIT_3004"
    MERGE_CONFLICT = "GIT_3005"
    BRANCH_EXISTS = "GIT_3006"
    BRANCH_NOT_FOUND = "GIT_3007"
    INVALID_REF = "GIT_3008"
    DIRTY_WORKSPACE = "GIT_3009"
    UNCOMMITTED_CHANGES = "GIT_3010"
    FETCH_FAILED = "GIT_3011"
    RESET_FAILED = "GIT_3012"

    # Provider errors (4xxx)
    PROVIDER_ERROR = "GIT_4000"
    PROVIDER_AUTH_FAILED = "GIT_4001"
    PROVIDER_NOT_FOUND = "GIT_4002"
    PR_CREATE_FAILED = "GIT_4003"
    PR_MERGE_FAILED = "GIT_4004"
    PR_NOT_FOUND = "GIT_4005"
    API_ERROR = "GIT_4006"

    # Credential errors (5xxx)
    CREDENTIAL_ERROR = "GIT_5000"
    CREDENTIAL_NOT_FOUND = "GIT_5001"
    CREDENTIAL_INVALID = "GIT_5002"
    SSH_KEY_ERROR = "GIT_5003"


class GitOpsError(Exception):
    """Base exception for Git Operations errors."""

    def __init__(
        self,
        message: str,
        code: ErrorCode = ErrorCode.INTERNAL_ERROR,
        details: dict[str, Any] | None = None,
    ) -> None:
        super().__init__(message)
        self.message = message
        self.code = code
        self.details = details or {}

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary for MCP response."""
        result: dict[str, Any] = {
            "error": self.message,
            "code": self.code.value,
        }
        if self.details:
            result["details"] = self.details
        return result


# Workspace Errors


class WorkspaceError(GitOpsError):
    """Base exception for workspace-related errors."""

    def __init__(
        self,
        message: str,
        code: ErrorCode = ErrorCode.WORKSPACE_NOT_FOUND,
        details: dict[str, Any] | None = None,
    ) -> None:
        super().__init__(message, code, details)


class WorkspaceNotFoundError(WorkspaceError):
    """Workspace does not exist."""

    def __init__(self, project_id: str) -> None:
        super().__init__(
            f"Workspace not found for project: {project_id}",
            ErrorCode.WORKSPACE_NOT_FOUND,
            {"project_id": project_id},
        )


class WorkspaceLockedError(WorkspaceError):
    """Workspace is locked by another operation."""

    def __init__(self, project_id: str, holder: str | None = None) -> None:
        details: dict[str, Any] = {"project_id": project_id}
        if holder:
            details["locked_by"] = holder
        super().__init__(
            f"Workspace is locked for project: {project_id}",
            ErrorCode.WORKSPACE_LOCKED,
            details,
        )


class WorkspaceSizeExceededError(WorkspaceError):
    """Workspace size limit exceeded."""

    def __init__(self, project_id: str, current_size: float, max_size: float) -> None:
        super().__init__(
            f"Workspace size limit exceeded for project: {project_id}",
            ErrorCode.WORKSPACE_SIZE_EXCEEDED,
            {
                "project_id": project_id,
                "current_size_gb": current_size,
                "max_size_gb": max_size,
            },
        )


# Git Operation Errors


class GitError(GitOpsError):
    """Base exception for git operation errors."""

    def __init__(
        self,
        message: str,
        code: ErrorCode = ErrorCode.INTERNAL_ERROR,
        details: dict[str, Any] | None = None,
    ) -> None:
        super().__init__(message, code, details)


class CloneError(GitError):
    """Failed to clone repository."""

    def __init__(self, repo_url: str, reason: str) -> None:
        super().__init__(
            f"Failed to clone repository: {reason}",
            ErrorCode.CLONE_FAILED,
            {"repo_url": repo_url, "reason": reason},
        )


class CheckoutError(GitError):
    """Failed to checkout branch or ref."""

    def __init__(self, ref: str, reason: str) -> None:
        super().__init__(
            f"Failed to checkout '{ref}': {reason}",
            ErrorCode.CHECKOUT_FAILED,
            {"ref": ref, "reason": reason},
        )


class CommitError(GitError):
    """Failed to commit changes."""

    def __init__(self, reason: str) -> None:
        super().__init__(
            f"Failed to commit: {reason}",
            ErrorCode.COMMIT_FAILED,
            {"reason": reason},
        )


class PushError(GitError):
    """Failed to push to remote."""

    def __init__(self, branch: str, reason: str) -> None:
        super().__init__(
            f"Failed to push branch '{branch}': {reason}",
            ErrorCode.PUSH_FAILED,
            {"branch": branch, "reason": reason},
        )


class PullError(GitError):
    """Failed to pull from remote."""

    def __init__(self, branch: str, reason: str) -> None:
        super().__init__(
            f"Failed to pull branch '{branch}': {reason}",
            ErrorCode.PULL_FAILED,
            {"branch": branch, "reason": reason},
        )


class MergeConflictError(GitError):
    """Merge conflict detected."""

    def __init__(self, conflicting_files: list[str]) -> None:
        super().__init__(
            f"Merge conflict detected in {len(conflicting_files)} files",
            ErrorCode.MERGE_CONFLICT,
            {"conflicting_files": conflicting_files},
        )


class BranchExistsError(GitError):
    """Branch already exists."""

    def __init__(self, branch_name: str) -> None:
        super().__init__(
            f"Branch already exists: {branch_name}",
            ErrorCode.BRANCH_EXISTS,
            {"branch": branch_name},
        )


class BranchNotFoundError(GitError):
    """Branch does not exist."""

    def __init__(self, branch_name: str) -> None:
        super().__init__(
            f"Branch not found: {branch_name}",
            ErrorCode.BRANCH_NOT_FOUND,
            {"branch": branch_name},
        )


class InvalidRefError(GitError):
    """Invalid git reference."""

    def __init__(self, ref: str) -> None:
        super().__init__(
            f"Invalid git reference: {ref}",
            ErrorCode.INVALID_REF,
            {"ref": ref},
        )


class DirtyWorkspaceError(GitError):
    """Workspace has uncommitted changes."""

    def __init__(self, modified_files: list[str]) -> None:
        super().__init__(
            f"Workspace has {len(modified_files)} uncommitted changes",
            ErrorCode.DIRTY_WORKSPACE,
            {"modified_files": modified_files[:10]},  # Limit to first 10
        )


# Provider Errors


class ProviderError(GitOpsError):
    """Base exception for provider-related errors."""

    def __init__(
        self,
        message: str,
        code: ErrorCode = ErrorCode.PROVIDER_ERROR,
        details: dict[str, Any] | None = None,
    ) -> None:
        super().__init__(message, code, details)


class AuthenticationError(ProviderError):
    """Authentication with provider failed."""

    def __init__(self, provider: str, reason: str) -> None:
        super().__init__(
            f"Authentication failed with {provider}: {reason}",
            ErrorCode.PROVIDER_AUTH_FAILED,
            {"provider": provider, "reason": reason},
        )


class ProviderNotFoundError(ProviderError):
    """Provider not configured or recognized."""

    def __init__(self, provider: str) -> None:
        super().__init__(
            f"Provider not found or not configured: {provider}",
            ErrorCode.PROVIDER_NOT_FOUND,
            {"provider": provider},
        )


class PRError(ProviderError):
    """Pull request operation failed."""

    def __init__(
        self,
        message: str,
        code: ErrorCode = ErrorCode.PR_CREATE_FAILED,
        details: dict[str, Any] | None = None,
    ) -> None:
        super().__init__(message, code, details)


class PRNotFoundError(PRError):
    """Pull request not found."""

    def __init__(self, pr_number: int, repo: str) -> None:
        super().__init__(
            f"Pull request #{pr_number} not found in {repo}",
            ErrorCode.PR_NOT_FOUND,
            {"pr_number": pr_number, "repo": repo},
        )


class APIError(ProviderError):
    """Provider API error."""

    def __init__(self, provider: str, status_code: int, message: str) -> None:
        super().__init__(
            f"{provider} API error ({status_code}): {message}",
            ErrorCode.API_ERROR,
            {"provider": provider, "status_code": status_code, "message": message},
        )


# Credential Errors


class CredentialError(GitOpsError):
    """Base exception for credential-related errors."""

    def __init__(
        self,
        message: str,
        code: ErrorCode = ErrorCode.CREDENTIAL_ERROR,
        details: dict[str, Any] | None = None,
    ) -> None:
        super().__init__(message, code, details)


class CredentialNotFoundError(CredentialError):
    """Credential not found."""

    def __init__(self, credential_type: str, identifier: str) -> None:
        super().__init__(
            f"{credential_type} credential not found: {identifier}",
            ErrorCode.CREDENTIAL_NOT_FOUND,
            {"type": credential_type, "identifier": identifier},
        )
