"""
Custom exceptions for jbqlab.

This module defines domain-specific exceptions for clearer error handling
and better user feedback.
"""


class JBQLabError(Exception):
    """Base exception for all jbqlab errors."""

    pass


class DataError(JBQLabError):
    """Errors related to data loading or validation."""

    pass


class DataFileNotFoundError(DataError):
    """Raised when the data file cannot be found."""

    def __init__(self, path: str):
        self.path = path
        super().__init__(f"Data file not found: {path}")


class DataValidationError(DataError):
    """Raised when data fails validation."""

    def __init__(self, message: str, errors: list[str] | None = None):
        self.errors = errors or []
        super().__init__(message)


class MissingColumnsError(DataValidationError):
    """Raised when required columns are missing from data."""

    def __init__(self, missing_columns: set[str]):
        self.missing_columns = missing_columns
        super().__init__(
            f"Missing required columns: {missing_columns}",
            errors=[f"Missing column: {col}" for col in missing_columns],
        )


class StrategyError(JBQLabError):
    """Errors related to strategy execution."""

    pass


class UnknownStrategyError(StrategyError):
    """Raised when an unknown strategy is requested."""

    def __init__(self, strategy_name: str, available: list[str]):
        self.strategy_name = strategy_name
        self.available = available
        super().__init__(
            f"Unknown strategy: '{strategy_name}'. "
            f"Available strategies: {', '.join(available)}"
        )


class StrategyParameterError(StrategyError):
    """Raised when strategy parameters are invalid."""

    def __init__(self, param_name: str, message: str):
        self.param_name = param_name
        super().__init__(f"Invalid parameter '{param_name}': {message}")


class BacktestError(JBQLabError):
    """Errors during backtest execution."""

    pass


class InsufficientDataError(BacktestError):
    """Raised when there isn't enough data to run the backtest."""

    def __init__(self, required: int, actual: int):
        self.required = required
        self.actual = actual
        super().__init__(
            f"Insufficient data: need at least {required} rows, got {actual}"
        )


class ConfigurationError(JBQLabError):
    """Errors in configuration or settings."""

    pass
