// 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 handling examples for SkyReels SDK
//!
//! This example demonstrates various error handling scenarios
//! and best practices for using the SDK.

use skyreels::{Error, SkyReelsClient};
use tracing::{error, info, warn};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Load environment variables from .env file
    dotenv::dotenv().ok();

    tracing_subscriber::fmt::init();

    let api_key = std::env::var("SKYREELS_API_KEY")
        .unwrap_or_else(|_| "test_api_key".to_string());

    let client = SkyReelsClient::new(&api_key);

    // Example 1: Handle missing API key
    example_invalid_parameters(&client).await;

    // Example 2: Handle network errors
    example_network_errors(&client).await;

    // Example 3: Handle API errors with status codes
    example_api_errors(&client).await;

    // Example 4: Retry logic for retryable errors
    example_retry_logic(&client).await;

    Ok(())
}

async fn example_invalid_parameters(client: &SkyReelsClient) {
    info!("=== Example 1: Invalid Parameters ===");

    // This will fail with invalid parameters
    match client
        .text2video()
        .get_task("invalid_task_id_format")
        .await
    {
        Ok(_) => info!("Unexpected success"),
        Err(Error::TaskNotFound(msg)) => {
            warn!("Task not found: {}", msg);
        }
        Err(Error::InvalidParameter(msg)) => {
            warn!("Invalid parameter: {}", msg);
        }
        Err(e) => {
            error!("Unexpected error: {}", e);
        }
    }
}

async fn example_network_errors(client: &SkyReelsClient) {
    info!("=== Example 2: Network Errors ===");

    // Create a client with an invalid base URL to simulate network errors
    let invalid_client = SkyReelsClient::with_base_url(
        "test_key",
        "https://invalid.nonexistent.domain",
    );

    match invalid_client
        .text2video()
        .get_task("some_task_id")
        .await
    {
        Ok(_) => info!("Unexpected success"),
        Err(Error::NetworkError(e)) => {
            warn!("Network error occurred: {}", e);
            info!("This is expected with an invalid URL");
        }
        Err(e) => {
            error!("Unexpected error type: {}", e);
        }
    }
}

async fn example_api_errors(client: &SkyReelsClient) {
    info!("=== Example 3: API Errors with Status Codes ===");

    match client
        .text2video()
        .get_task("nonexistent_task")
        .await
    {
        Ok(_) => info!("Unexpected success"),
        Err(Error::ApiError { status, message }) => {
            warn!("API error occurred");
            warn!("Status code: {}", status);
            warn!("Message: {}", message);

            // Handle different status codes
            match status {
                401 => warn!("Authentication failed - check your API key"),
                404 => warn!("Resource not found - check the task ID"),
                429 => warn!("Rate limit exceeded - please wait before retrying"),
                500..=599 => warn!("Server error - the service may be temporarily unavailable"),
                _ => warn!("Unexpected status code"),
            }
        }
        Err(e) => {
            error!("Unexpected error type: {}", e);
        }
    }
}

async fn example_retry_logic(client: &SkyReelsClient) {
    info!("=== Example 4: Retry Logic ===");

    let task_id = "some_task_id";
    let max_retries = 3;
    let mut attempts = 0;

    loop {
        attempts += 1;

        match client.text2video().get_task(task_id).await {
            Ok(response) => {
                info!("Request succeeded on attempt {}", attempts);
                info!("Task status: {}", response.get_status());
                break;
            }
            Err(e) => {
                error!("Attempt {} failed: {}", attempts, e);

                // Check if error is retryable
                if !e.is_retryable() {
                    error!("Error is not retryable, giving up");
                    break;
                }

                if attempts >= max_retries {
                    error!("Max retry attempts reached, giving up");
                    break;
                }

                // Get status code if available
                if let Some(status) = e.status_code() {
                    warn!("Status code: {}", status);
                }

                // Wait before retrying (exponential backoff)
                let wait_time = 2_u64.pow(attempts as u32);
                info!("Waiting {} seconds before retry...", wait_time);
                tokio::time::sleep(tokio::time::Duration::from_secs(wait_time)).await;
            }
        }
    }
}

// Example of a robust helper function with error handling
async fn robust_submit_task(
    client: &SkyReelsClient,
    prompt: &str,
    max_retries: u32,
) -> Result<String, Error> {
    let mut attempts = 0;

    loop {
        attempts += 1;

        // Create the request
        let request = client.text2video().submit_request(prompt);

        match client.text2video().submit(request).await {
            Ok(task_id) => {
                info!("Task submitted successfully on attempt {}", attempts);
                return Ok(task_id);
            }
            Err(e) => {
                warn!("Submit attempt {} failed: {}", attempts, e);

                // Check if error is retryable
                if !e.is_retryable() || attempts >= max_retries {
                    return Err(e);
                }

                // Exponential backoff
                let wait_time = 2_u64.pow(attempts.min(5) as u32);
                tokio::time::sleep(tokio::time::Duration::from_secs(wait_time)).await;
            }
        }
    }
}
