//! Storage backend trait definition

use async_trait::async_trait;
use bytes::Bytes;
use lightningstor_types::ObjectId;
use std::io;
use thiserror::Error;

/// Storage backend error
#[derive(Debug, Error)]
pub enum StorageError {
    #[error("IO error: {0}")]
    Io(#[from] io::Error),
    
    #[error("Object not found: {0}")]
    NotFound(ObjectId),
    
    #[error("Storage backend error: {0}")]
    Backend(String),
    
    #[error("Invalid object ID: {0}")]
    InvalidObjectId(String),
}

/// Storage result type
pub type StorageResult<T> = std::result::Result<T, StorageError>;

impl From<StorageError> for lightningstor_types::Error {
    fn from(err: StorageError) -> Self {
        match err {
            StorageError::Io(e) => lightningstor_types::Error::StorageError(e.to_string()),
            StorageError::NotFound(id) => {
                lightningstor_types::Error::ObjectNotFound {
                    bucket: String::new(),
                    key: id.to_string(),
                }
            }
            StorageError::Backend(msg) => lightningstor_types::Error::StorageError(msg),
            StorageError::InvalidObjectId(msg) => {
                lightningstor_types::Error::InvalidArgument(msg)
            }
        }
    }
}

/// Storage backend trait for object data storage
///
/// This trait abstracts the storage of object data, allowing different
/// implementations (local filesystem, distributed storage, cloud storage).
#[async_trait]
pub trait StorageBackend: Send + Sync {
    /// Write object data
    ///
    /// # Arguments
    /// * `object_id` - Internal object identifier
    /// * `data` - Object data bytes
    ///
    /// # Returns
    /// * `Ok(())` if write succeeded
    /// * `Err(StorageError)` if write failed
    async fn put_object(&self, object_id: &ObjectId, data: Bytes) -> StorageResult<()>;
    
    /// Read object data
    ///
    /// # Arguments
    /// * `object_id` - Internal object identifier
    ///
    /// # Returns
    /// * `Ok(Bytes)` if object exists
    /// * `Err(StorageError::NotFound)` if object does not exist
    /// * `Err(StorageError)` for other errors
    async fn get_object(&self, object_id: &ObjectId) -> StorageResult<Bytes>;
    
    /// Delete object data
    ///
    /// # Arguments
    /// * `object_id` - Internal object identifier
    ///
    /// # Returns
    /// * `Ok(())` if delete succeeded (or object didn't exist)
    /// * `Err(StorageError)` for other errors
    async fn delete_object(&self, object_id: &ObjectId) -> StorageResult<()>;
    
    /// Check if object exists
    ///
    /// # Arguments
    /// * `object_id` - Internal object identifier
    ///
    /// # Returns
    /// * `Ok(true)` if object exists
    /// * `Ok(false)` if object does not exist
    /// * `Err(StorageError)` for other errors
    async fn object_exists(&self, object_id: &ObjectId) -> StorageResult<bool>;
    
    /// Get object size in bytes
    ///
    /// # Arguments
    /// * `object_id` - Internal object identifier
    ///
    /// # Returns
    /// * `Ok(u64)` if object exists
    /// * `Err(StorageError::NotFound)` if object does not exist
    /// * `Err(StorageError)` for other errors
    async fn object_size(&self, object_id: &ObjectId) -> StorageResult<u64>;
    
    /// Write part data (for multipart uploads)
    ///
    /// # Arguments
    /// * `upload_id` - Multipart upload ID
    /// * `part_number` - Part number (1-based)
    /// * `data` - Part data bytes
    ///
    /// # Returns
    /// * `Ok(())` if write succeeded
    /// * `Err(StorageError)` if write failed
    async fn put_part(
        &self,
        upload_id: &str,
        part_number: u32,
        data: Bytes,
    ) -> StorageResult<()>;
    
    /// Read part data (for multipart uploads)
    ///
    /// # Arguments
    /// * `upload_id` - Multipart upload ID
    /// * `part_number` - Part number (1-based)
    ///
    /// # Returns
    /// * `Ok(Bytes)` if part exists
    /// * `Err(StorageError::NotFound)` if part does not exist
    /// * `Err(StorageError)` for other errors
    async fn get_part(
        &self,
        upload_id: &str,
        part_number: u32,
    ) -> StorageResult<Bytes>;
    
    /// Delete part data (for multipart uploads)
    ///
    /// # Arguments
    /// * `upload_id` - Multipart upload ID
    /// * `part_number` - Part number (1-based)
    ///
    /// # Returns
    /// * `Ok(())` if delete succeeded
    /// * `Err(StorageError)` for other errors
    async fn delete_part(&self, upload_id: &str, part_number: u32) -> StorageResult<()>;
    
    /// Delete all parts for a multipart upload
    ///
    /// # Arguments
    /// * `upload_id` - Multipart upload ID
    ///
    /// # Returns
    /// * `Ok(())` if delete succeeded
    /// * `Err(StorageError)` for other errors
    async fn delete_upload_parts(&self, upload_id: &str) -> StorageResult<()>;
}
