"""Custom exceptions for flukebase_connect.

Provides rich error classes with:
- Error codes for programmatic handling
- Human-readable messages
- Root cause identification
- Actionable solution suggestions
"""

from dataclasses import dataclass
from typing import List, Optional

DOCS_BASE_URL = "https://flukebase.me/docs"


@dataclass
class Solution:
    """An actionable solution for an error."""

    description: str
    command: Optional[str] = None
    priority: int = 0


class FlukeBaseError(Exception):
    """Base exception with rich error support."""

    code: str = "ERR_000"
    docs_path: Optional[str] = None

    def __init__(
        self,
        message: str,
        *,
        code: Optional[str] = None,
        cause: Optional[str] = None,
        solutions: Optional[List[Solution]] = None,
        docs_path: Optional[str] = None,
    ):
        self.message = message
        if code:
            self.code = code
        self.cause = cause
        self.solutions = solutions or []
        if docs_path:
            self.docs_path = docs_path
        super().__init__(message)

    @property
    def docs_url(self) -> Optional[str]:
        """Full documentation URL."""
        if self.docs_path:
            return f"{DOCS_BASE_URL}/{self.docs_path}"
        return None

    def format(self, verbose: bool = False, color: bool = True) -> str:
        """Format error for display."""
        RED = "\033[91m" if color else ""
        YELLOW = "\033[93m" if color else ""
        CYAN = "\033[96m" if color else ""
        BOLD = "\033[1m" if color else ""
        RESET = "\033[0m" if color else ""

        lines = [f"{RED}{BOLD}Error [{self.code}]{RESET}: {self.message}"]

        if self.cause:
            lines.append(f"\n{YELLOW}Cause:{RESET} {self.cause}")

        if self.solutions:
            lines.append(f"\n{CYAN}Solutions:{RESET}")
            sorted_solutions = sorted(self.solutions, key=lambda s: -s.priority)
            for i, sol in enumerate(sorted_solutions, 1):
                lines.append(f"  {i}. {sol.description}")
                if sol.command:
                    lines.append(f"     → {BOLD}{sol.command}{RESET}")

        if self.docs_url and verbose:
            lines.append(f"\n{CYAN}Docs:{RESET} {self.docs_url}")

        return "\n".join(lines)

    def __str__(self) -> str:
        return self.format(color=False)


class AuthError(FlukeBaseError):
    """Authentication error."""

    code = "AUTH_001"
    docs_path = "auth/troubleshooting"

    def __init__(
        self,
        message: str = "Authentication failed",
        *,
        cause: Optional[str] = None,
        **kwargs,
    ):
        solutions = [
            Solution("Run 'fbc login' to refresh your token", "fbc login", 3),
            Solution("Check your token at https://flukebase.me/settings/tokens", priority=2),
        ]
        super().__init__(message, cause=cause, solutions=solutions, **kwargs)


class NotFoundError(FlukeBaseError):
    """Resource not found error."""

    code = "NOT_FOUND_001"


class ApiConnectionError(FlukeBaseError):
    """Connection error."""

    code = "CONN_001"
    docs_path = "troubleshooting/connection"

    def __init__(
        self,
        message: str = "Failed to connect to FlukeBase API",
        *,
        cause: Optional[str] = None,
        **kwargs,
    ):
        solutions = [
            Solution("Check your internet connection", priority=3),
            Solution("Verify FlukeBase API status", "fbc status", 2),
        ]
        super().__init__(message, cause=cause, solutions=solutions, **kwargs)


class ToolExecutionError(FlukeBaseError):
    """Remote tool execution error."""

    code = "TOOL_001"

    def __init__(
        self,
        tool_name: str,
        message: str,
        *,
        cause: Optional[str] = None,
        **kwargs,
    ):
        self.tool_name = tool_name
        solutions = [
            Solution("Check your connection status", "fbc status", 3),
            Solution("Verify the tool exists", "fbc tools", 2),
        ]
        super().__init__(
            f"Tool '{tool_name}' execution failed: {message}",
            cause=cause,
            solutions=solutions,
            **kwargs,
        )
