// Copyright (c) 2026, Salesforce, Inc.,
// All rights reserved.
// For full license text, see the LICENSE.txt file

//! Error types for distributed storage operations
//!
//! Defines [`DistributedStorageError`], the error surface returned by the
//! distributed storage client and trait implementations. Error mapping from
//! HTTP responses is also provided.

use std::fmt::Debug;

use pdk_core::classy::hl::{HttpClientError, HttpClientResponse};
use serde::Deserialize;
use thiserror::Error;

const TIMEOUT: u32 = 504;
const PRECONDITION_FAILED: u32 = 412;

#[non_exhaustive]
#[derive(Error, Debug)]
/// Errors that can occur during distributed storage operations.
pub enum DistributedStorageError {
    /// Indicates the requested key was not found.
    #[error("Key not found.")]
    KeyNotFound,
    /// Indicates the requested store was not found.
    #[error("Store not found.")]
    StoreNotFound,
    /// Indicates the operation timed out.
    #[error("Timeout.")]
    Timeout,
    /// Indicates the operation was aborted due to a difference in the expected CAS value.
    #[error("Cas Mismatch.")]
    CasMismatch,
    /// Indicates an error occurred while performing an http client call.
    #[error("Error performing http client call: {0}.")]
    HttpClient(#[from] HttpClientError),
    /// Indicates an unexpected error occurred.
    #[error("Unexpected error: {0}.")]
    Unexpected(String),
}

#[derive(Deserialize, Debug)]
struct PayloadSharedStorageHttpError {
    pub message: String,
}

impl From<HttpClientResponse> for DistributedStorageError {
    fn from(response: HttpClientResponse) -> Self {
        if response.status_code() == PRECONDITION_FAILED {
            return DistributedStorageError::CasMismatch;
        }

        if response.status_code() == TIMEOUT {
            return DistributedStorageError::Timeout;
        }

        if let Ok(error) = serde_json::from_slice::<PayloadSharedStorageHttpError>(response.body())
        {
            let message = error.message.to_lowercase();
            let message = message.trim_end_matches(".");
            if message.starts_with("store") && message.ends_with("not found") {
                return DistributedStorageError::StoreNotFound;
            } else if message.starts_with("object with key") && message.ends_with("not found") {
                return DistributedStorageError::KeyNotFound;
            } else {
                return DistributedStorageError::Unexpected(error.message);
            }
        }

        DistributedStorageError::Unexpected(format!(
            "Unexpected Response:\n\tHeaders: {:?}\n\tBody:{}",
            response.headers(),
            String::from_utf8_lossy(response.body())
        ))
    }
}
