// Copyright 2025 SkyReels SDK Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/// Error type for SkyReels SDK operations
#[derive(Debug, thiserror::Error)]
pub enum Error {
    /// API request failed with a specific status code
    #[error("API request failed with status {status}: {message}")]
    ApiError { status: u16, message: String },

    /// Network or HTTP error
    #[error("Network error: {0}")]
    NetworkError(#[from] reqwest::Error),

    /// Invalid parameter provided
    #[error("Invalid parameter: {0}")]
    InvalidParameter(String),

    /// API key not configured
    #[error("API key not configured")]
    MissingApiKey,

    /// Task submission failed
    #[error("Task submission failed: {0}")]
    SubmissionFailed(String),

    /// Task query failed
    #[error("Task query failed: {0}")]
    QueryFailed(String),

    /// Response parsing error
    #[error("Failed to parse response: {0}")]
    ResponseParseError(String),

    /// Task not found or invalid task ID
    #[error("Task not found: {0}")]
    TaskNotFound(String),

    /// Rate limit exceeded
    #[error("Rate limit exceeded")]
    RateLimitExceeded,

    /// Authentication failed
    #[error("Authentication failed")]
    AuthenticationFailed,
}

/// Result type alias for SkyReels SDK operations
pub type Result<T> = std::result::Result<T, Error>;

impl Error {
    /// Create a new API error from status code and message
    pub fn api_error(status: u16, message: impl Into<String>) -> Self {
        Error::ApiError {
            status,
            message: message.into(),
        }
    }

    /// Create a new invalid parameter error
    pub fn invalid_parameter(param: impl Into<String>) -> Self {
        Error::InvalidParameter(param.into())
    }

    /// Create a new task not found error
    pub fn task_not_found(task_id: impl Into<String>) -> Self {
        Error::TaskNotFound(task_id.into())
    }

    /// Check if error is retryable
    pub fn is_retryable(&self) -> bool {
        matches!(
            self,
            Error::NetworkError(_)
                | Error::RateLimitExceeded
                | Error::ApiError { status: 500..=599, .. }
        )
    }

    /// Get HTTP status code if available
    pub fn status_code(&self) -> Option<u16> {
        match self {
            Error::ApiError { status, .. } => Some(*status),
            Error::RateLimitExceeded => Some(429),
            Error::AuthenticationFailed => Some(401),
            _ => None,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_error_creation() {
        let err = Error::api_error(500, "Internal server error");
        assert!(err.is_retryable());
        assert_eq!(err.status_code(), Some(500));
    }

    #[test]
    fn test_invalid_parameter() {
        let err = Error::invalid_parameter("duration must be between 3 and 15");
        assert!(!err.is_retryable());
        assert_eq!(err.status_code(), None);
    }

    #[test]
    fn test_rate_limit() {
        let err = Error::RateLimitExceeded;
        assert!(err.is_retryable());
        assert_eq!(err.status_code(), Some(429));
    }
}
