use super::{S3PutRequest, StorageAdaptor, StoredObject};
use crate::error::TrazaeoResult;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DeterministicS3StorageAdaptor {
    pub bucket: String,
    pub base_prefix: Option<String>,
}

impl DeterministicS3StorageAdaptor {
    pub fn new(bucket: &str, base_prefix: Option<&str>) -> Self {
        Self {
            bucket: bucket.to_string(),
            base_prefix: base_prefix.map(str::to_string),
        }
    }

    pub fn build_key(&self, object_name: &str) -> String {
        let prefix = self.base_prefix.as_deref().unwrap_or("").trim_matches('/');
        if prefix.is_empty() {
            object_name.trim_start_matches('/').to_string()
        } else {
            format!("{prefix}/{}", object_name.trim_start_matches('/'))
        }
    }

    pub fn build_uri(&self, key: &str) -> String {
        format!("s3://{}/{}", self.bucket, key.trim_start_matches('/'))
    }
}

impl StorageAdaptor for DeterministicS3StorageAdaptor {
    fn put_bytes(&self, request: &S3PutRequest) -> TrazaeoResult<StoredObject> {
        let hash = blake3::hash(&request.bytes);
        Ok(StoredObject {
            key: request.key.clone(),
            uri: self.build_uri(&request.key),
            content_hash: hex::encode(hash.as_bytes()),
            byte_length: request.bytes.len(),
            content_type: request.content_type.clone(),
        })
    }
}
