"""Custom exceptions for SLTK.

This module provides a hierarchical exception system for the SLTK library,
enabling more precise error handling and meaningful error messages.
"""


class SLTKError(Exception):
    """Base exception for all SLTK errors.

    All custom exceptions in SLTK inherit from this class, making it easy
    to catch any SLTK-specific error with a single except clause.
    """

    pass


class ConfigurationError(SLTKError):
    """Configuration or setup errors.

    Raised when there are issues with configuration settings, environment
    setup, or invalid parameter combinations.
    """

    pass


class DatasetError(SLTKError):
    """Dataset loading or access errors.

    Base class for all dataset-related errors including loading failures,
    missing data, and access issues.
    """

    pass


class SampleNotFoundError(DatasetError):
    """Requested sample not found in dataset.

    Raised when attempting to access a sample that doesn't exist in the dataset.

    Attributes:
        sample_id: The ID of the sample that was not found.
        dataset: The name of the dataset (optional).
    """

    def __init__(self, sample_id: str, dataset: str = ""):
        self.sample_id = sample_id
        self.dataset = dataset
        msg = f"Sample '{sample_id}' not found"
        if dataset:
            msg += f" in {dataset}"
        super().__init__(msg)


class FeatureNotFoundError(DatasetError):
    """Requested feature not available.

    Raised when attempting to access a feature that doesn't exist or
    hasn't been extracted for a given sample or dataset.

    Attributes:
        feature_name: The name of the feature that was not found.
        sample_id: The sample ID (optional).
        dataset: The dataset name (optional).
    """

    def __init__(self, feature_name: str, sample_id: str | None = None, dataset: str | None = None):
        self.feature_name = feature_name
        self.sample_id = sample_id
        self.dataset = dataset
        msg = f"Feature '{feature_name}' not found"
        if sample_id:
            msg += f" for sample '{sample_id}'"
        if dataset:
            msg += f" in {dataset}"
        super().__init__(msg)


class DatasetNotFoundError(DatasetError):
    """Dataset not found or not accessible.

    Raised when the requested dataset doesn't exist or cannot be accessed.

    Attributes:
        dataset_name: The name of the dataset that was not found.
        path: The path where the dataset was expected (optional).
    """

    def __init__(self, dataset_name: str, path: str | None = None):
        self.dataset_name = dataset_name
        self.path = path
        msg = f"Dataset '{dataset_name}' not found"
        if path:
            msg += f" at '{path}'"
        super().__init__(msg)


class ExtractionError(SLTKError):
    """Pose extraction failures.

    Raised when pose extraction fails due to processing errors,
    invalid input, or model failures.
    """

    pass


class ExtractionCancelled(ExtractionError):
    """Extraction was cancelled by user.

    Raised when an extraction operation is interrupted or cancelled
    by the user or system.
    """

    pass


class ModelNotFoundError(ExtractionError):
    """Required model weights not found.

    Raised when model weights or checkpoints required for extraction
    are not available.

    Attributes:
        model_name: The name of the model.
        path: The expected path for the model weights (optional).
    """

    def __init__(self, model_name: str, path: str | None = None):
        self.model_name = model_name
        self.path = path
        msg = f"Model '{model_name}' not found"
        if path:
            msg += f" at '{path}'"
        super().__init__(msg)


class ValidationError(SLTKError):
    """Data validation failures.

    Raised when data fails validation checks, such as shape mismatches,
    invalid values, or constraint violations.
    """

    pass


class FormatError(SLTKError):
    """File format or conversion errors.

    Raised when there are issues with file formats, unsupported formats,
    or conversion failures between formats.
    """

    pass


class UnsupportedFormatError(FormatError):
    """Unsupported file format.

    Raised when attempting to use a file format that is not supported.

    Attributes:
        format_name: The name of the unsupported format.
        supported_formats: List of supported formats (optional).
    """

    def __init__(self, format_name: str, supported_formats: list | None = None):
        self.format_name = format_name
        self.supported_formats = supported_formats
        msg = f"Unsupported format: '{format_name}'"
        if supported_formats:
            msg += f". Supported formats: {', '.join(supported_formats)}"
        super().__init__(msg)


class SLTKIOError(SLTKError):
    """Input/Output operation errors.

    Raised when there are issues reading or writing files.
    """

    pass


class SLTKFileNotFoundError(SLTKIOError):
    """File not found error.

    Raised when a required file cannot be found.

    Attributes:
        file_path: The path to the file that was not found.
    """

    def __init__(self, file_path: str):
        self.file_path = file_path
        super().__init__(f"File not found: '{file_path}'")


class MetricsError(SLTKError):
    """Metrics computation errors.

    Raised when there are issues computing metrics, such as
    incompatible inputs or missing dependencies.
    """

    pass


class DependencyError(SLTKError):
    """Missing or incompatible dependency.

    Raised when a required dependency is not installed or
    is incompatible with the current environment.

    Attributes:
        dependency_name: The name of the missing dependency.
        install_hint: Installation instructions (optional).
    """

    def __init__(self, dependency_name: str, install_hint: str | None = None):
        self.dependency_name = dependency_name
        self.install_hint = install_hint
        msg = f"Missing dependency: '{dependency_name}'"
        if install_hint:
            msg += f". Install with: {install_hint}"
        super().__init__(msg)


class AnnotationError(SLTKError):
    """Annotation-related errors.

    Raised when there are issues with annotations, such as
    invalid tier names or malformed annotation data.
    """

    pass


class SegmentationError(SLTKError):
    """Segmentation-related errors.

    Raised when there are issues with temporal segmentation.
    """

    pass


# Export all exceptions
__all__ = [
    "SLTKError",
    "ConfigurationError",
    "DatasetError",
    "SampleNotFoundError",
    "FeatureNotFoundError",
    "DatasetNotFoundError",
    "ExtractionError",
    "ExtractionCancelled",
    "ModelNotFoundError",
    "ValidationError",
    "FormatError",
    "UnsupportedFormatError",
    "SLTKIOError",
    "SLTKFileNotFoundError",
    "MetricsError",
    "DependencyError",
    "AnnotationError",
    "SegmentationError",
]
