//! Error types for data acquisition service

use thiserror::Error;

/// Result type alias for acquisition operations
pub type AcquisitionResult<T> = Result<T, AcquisitionError>;

/// Errors that can occur during data acquisition
#[derive(Error, Debug)]
pub enum AcquisitionError {
    /// Network connectivity issues
    #[error("Network error: {message}")]
    Network { message: String },

    /// Databento API errors
    #[error("Databento API error: {message}")]
    DatabentorAPI { message: String },

    /// Authentication failures
    #[error("Authentication failed: {message}")]
    Authentication { message: String },

    /// Rate limiting errors
    #[error("Rate limit exceeded: {message}")]
    RateLimit { message: String },

    /// Data validation errors
    #[error("Data validation failed: {message}")]
    Validation { message: String },

    /// Storage/upload errors
    #[error("Storage error: {message}")]
    Storage { message: String },

    /// Database errors
    #[error("Database error: {message}")]
    Database { message: String },

    /// Configuration errors
    #[error("Configuration error: {message}")]
    Config { message: String },

    /// Disk space errors
    #[error("Disk space exhausted: {message}")]
    DiskSpace { message: String },

    /// Timeout errors
    #[error("Operation timed out: {message}")]
    Timeout { message: String },

    /// Data corruption detected
    #[error("Data corruption detected: {message}")]
    DataCorruption { message: String },

    /// Invalid request parameters
    #[error("Invalid request: {message}")]
    InvalidRequest { message: String },

    /// Job not found
    #[error("Job not found: {job_id}")]
    JobNotFound { job_id: String },

    /// Generic internal error
    #[error("Internal error: {message}")]
    Internal { message: String },
}

// Conversions from other error types
impl From<std::io::Error> for AcquisitionError {
    fn from(err: std::io::Error) -> Self {
        AcquisitionError::Internal {
            message: err.to_string(),
        }
    }
}

impl From<reqwest::Error> for AcquisitionError {
    fn from(err: reqwest::Error) -> Self {
        if err.is_timeout() {
            AcquisitionError::Timeout {
                message: err.to_string(),
            }
        } else if err.is_connect() {
            AcquisitionError::Network {
                message: err.to_string(),
            }
        } else {
            AcquisitionError::Internal {
                message: err.to_string(),
            }
        }
    }
}

impl From<storage::StorageError> for AcquisitionError {
    fn from(err: storage::StorageError) -> Self {
        AcquisitionError::Storage {
            message: err.to_string(),
        }
    }
}

impl From<sqlx::Error> for AcquisitionError {
    fn from(err: sqlx::Error) -> Self {
        AcquisitionError::Database {
            message: err.to_string(),
        }
    }
}

impl From<config::ConfigError> for AcquisitionError {
    fn from(err: config::ConfigError) -> Self {
        AcquisitionError::Config {
            message: err.to_string(),
        }
    }
}

// Convert to tonic Status for gRPC
impl From<AcquisitionError> for tonic::Status {
    fn from(err: AcquisitionError) -> Self {
        match err {
            AcquisitionError::InvalidRequest { .. } => {
                tonic::Status::invalid_argument(err.to_string())
            },
            AcquisitionError::JobNotFound { .. } => tonic::Status::not_found(err.to_string()),
            AcquisitionError::Authentication { .. } => {
                tonic::Status::unauthenticated(err.to_string())
            },
            AcquisitionError::RateLimit { .. } => {
                tonic::Status::resource_exhausted(err.to_string())
            },
            _ => tonic::Status::internal(err.to_string()),
        }
    }
}
