"""Exceptions for fair task queue."""

from typing import Any

from haolib.exceptions.base.abstract import AbstractException


class FairQueueError(AbstractException):
    """Base exception for fair queue errors."""

    detail: str = "Fair queue error"

    def __init__(
        self,
        *args: Any,
        detail: str | None = None,
        task_id: str | None = None,
        group_id: str | None = None,
        **kwargs: Any,
    ) -> None:
        """Initialize fair queue error.

        Args:
            *args: Additional args
            detail: Error detail message
            task_id: Optional task ID related to the error
            group_id: Optional group ID related to the error
            **kwargs: Additional kwargs

        """
        if detail:
            self.detail = detail
        super().__init__(*args, **kwargs)
        self.task_id = task_id
        self.group_id = group_id


class FairQueueQueueFullError(FairQueueError):
    """Raised when queue size limit is reached."""

    detail: str = "Queue is full"

    def __init__(
        self,
        *args: Any,
        detail: str | None = None,
        task_id: str | None = None,
        group_id: str | None = None,
        queue_length: int | None = None,
        max_size: int | None = None,
        **kwargs: Any,
    ) -> None:
        """Initialize queue full error.

        Args:
            *args: Additional args
            detail: Error detail message
            task_id: Optional task ID
            group_id: Optional group ID
            queue_length: Current queue length
            max_size: Maximum queue size
            **kwargs: Additional kwargs

        """
        # Build actionable error message
        if detail is None:
            if queue_length is not None and max_size is not None:
                detail = (
                    f"Queue is full: {queue_length}/{max_size} tasks. "
                    f"Recovery options: 1) Enable backpressure (options.backpressure=True) to wait for space, "
                    f"2) Increase max_queue_size_per_group or max_global_queue_size, "
                    f"3) Process tasks faster by increasing scheduler_max_concurrent_tasks, "
                    f"4) Check for stuck tasks with queue.recover_stuck_tasks()"
                )
            else:
                detail = (
                    "Queue is full. Recovery options: 1) Enable backpressure (options.backpressure=True), "
                    "2) Increase queue size limits, 3) Process tasks faster, 4) Check for stuck tasks"
                )

        super().__init__(*args, detail=detail, task_id=task_id, group_id=group_id, **kwargs)
        self.queue_length = queue_length
        self.max_size = max_size


class FairQueueInvalidTaskError(FairQueueError):
    """Raised when task validation fails."""

    detail: str = "Invalid task"

    def __init__(
        self,
        *args: Any,
        detail: str | None = None,
        task_id: str | None = None,
        group_id: str | None = None,
        validation_error: str | None = None,
        **kwargs: Any,
    ) -> None:
        """Initialize invalid task error.

        Args:
            *args: Additional args
            detail: Error detail message
            task_id: Optional task ID
            group_id: Optional group ID
            validation_error: Validation error details
            **kwargs: Additional kwargs

        """
        # Build actionable error message based on validation error type
        if detail is None or detail == "Invalid task":
            if validation_error:
                error_guidance = {
                    "priority out of range": (
                        "Adjust priority to be within allowed range (check min_priority/max_priority)"
                    ),
                    "group_id too long": "Shorten group_id or increase max_group_id_length",
                    "circular_dependency": "Remove circular dependencies from depends_on list",
                    "duplicate_dependencies": "Remove duplicate task IDs from depends_on list",
                    "too_many_dependencies": "Reduce number of dependencies or increase max_dependency_depth",
                    "invalid_dependency_id": "Ensure all dependency task IDs are valid strings",
                    "dependency_id_too_long": "Shorten dependency task IDs or increase max_dependency_task_id_length",
                }
                guidance = error_guidance.get(validation_error, "Review task configuration and fix validation issues")
                detail = f"Task validation failed ({validation_error}): {guidance}"
            else:
                detail = "Task validation failed. Review task configuration and ensure all required fields are valid"

        super().__init__(*args, detail=detail, task_id=task_id, group_id=group_id, **kwargs)
        self.validation_error = validation_error


class FairQueueStorageError(FairQueueError):
    """Raised when storage operation fails."""

    detail: str = "Storage operation failed"

    def __init__(
        self,
        *args: Any,
        detail: str | None = None,
        task_id: str | None = None,
        group_id: str | None = None,
        operation: str | None = None,
        **kwargs: Any,
    ) -> None:
        """Initialize storage error.

        Args:
            *args: Additional args
            detail: Error detail message
            task_id: Optional task ID
            group_id: Optional group ID
            operation: Storage operation that failed
            **kwargs: Additional kwargs

        """
        # Build actionable error message if not provided
        if detail is None or detail == "Storage operation failed":
            base_msg = "Storage operation failed"
            if operation:
                base_msg += f" during {operation}"
            detail = (
                f"{base_msg}. Recovery steps: 1) Check storage health: queue.health_check(), "
                f"2) Verify storage backend is running and accessible, "
                f"3) Check network connectivity, 4) Verify storage capacity (disk space, memory), "
                f"5) Review storage logs for detailed error information"
            )

        super().__init__(*args, detail=detail, task_id=task_id, group_id=group_id, **kwargs)
        self.operation = operation


class FairQueuePartialBatchError(FairQueueError):
    """Raised when some tasks in a batch failed to enqueue."""

    detail: str = "Some tasks in batch failed to enqueue"

    def __init__(
        self,
        *args: Any,
        detail: str | None = None,
        task_id: str | None = None,
        group_id: str | None = None,
        failed_count: int | None = None,
        total_count: int | None = None,
        errors: list[tuple[int, Exception]] | None = None,
        **kwargs: Any,
    ) -> None:
        """Initialize partial batch error.

        Args:
            *args: Additional args
            detail: Error detail message
            task_id: Optional task ID
            group_id: Optional group ID
            failed_count: Number of failed tasks
            total_count: Total number of tasks
            errors: List of (index, error) tuples
            **kwargs: Additional kwargs

        """
        super().__init__(*args, detail=detail, task_id=task_id, group_id=group_id, **kwargs)
        self.failed_count = failed_count
        self.total_count = total_count
        self.errors = errors or []


class FairQueueIdempotencyConflictError(FairQueueError):
    """Raised when idempotency key conflict occurs."""

    detail: str = "Task with idempotency key already exists"

    def __init__(
        self, task_id: str, idempotency_key: str, *args: Any, group_id: str | None = None, **kwargs: Any
    ) -> None:
        """Initialize idempotency conflict error.

        Args:
            task_id: Existing task ID
            idempotency_key: Idempotency key that caused conflict
            *args: Additional args
            group_id: Optional group ID
            **kwargs: Additional kwargs

        """
        super().__init__(*args, task_id=task_id, group_id=group_id, **kwargs)
        self.idempotency_key = idempotency_key


class FairQueueCircuitBreakerOpenError(FairQueueError):
    """Raised when circuit breaker is open and request cannot proceed."""

    detail: str = "Circuit breaker is open, request rejected"


class FairQueueSerializationError(FairQueueError):
    """Raised when task serialization fails."""

    detail: str = "Task serialization failed"

    def __init__(
        self,
        *args: Any,
        detail: str | None = None,
        task_id: str | None = None,
        serialization_strategy: str | None = None,
        serialization_error: Exception | None = None,
        **kwargs: Any,
    ) -> None:
        """Initialize serialization error.

        Args:
            *args: Additional args
            detail: Error detail message
            task_id: Optional task ID
            serialization_strategy: Serialization strategy that failed
            serialization_error: Original exception that caused the failure
            **kwargs: Additional kwargs

        """
        if detail is None or detail == "Task serialization failed":
            parts = ["Task serialization failed"]

            if serialization_strategy:
                parts.append(f"Strategy: {serialization_strategy}")

            if serialization_error:
                parts.append(f"Error: {serialization_error}")

            parts.append(
                "Recovery options: "
                "1) Use a different serialization_strategy (JSON, PICKLE, MSGPACK), "
                "2) Ensure task arguments are serializable, "
                "3) Use simpler data types (dicts, lists, primitives) instead of complex objects"
            )

            detail = ". ".join(parts)

        super().__init__(*args, detail=detail, task_id=task_id, **kwargs)
        self.serialization_strategy = serialization_strategy
        self.serialization_error = serialization_error
