use crate::storage::error::StorageError;
use async_trait::async_trait;
use hive_router_config::storage::{StorageConfigMap, StorageSourceConfig};
use hive_router_internal::telemetry::logging::targets;
use object_store::path::Path;
use std::{collections::HashMap, sync::Arc};
use tracing::debug;

pub mod error;
pub mod s3_runtime;
pub mod utils;

pub struct StorageManager {
    storage_runtimes: HashMap<String, Arc<Box<dyn StorageRuntime>>>,
}

impl StorageManager {
    pub fn new(config_map: &StorageConfigMap) -> Result<Self, StorageError> {
        let mut storage_runtimes: HashMap<String, Arc<Box<dyn StorageRuntime>>> = HashMap::new();

        for (id, config) in config_map {
            debug!(target: targets::STORAGE, storage_id = id, config = ?config, "creating storage runtime");

            storage_runtimes.insert(
                id.to_string(),
                Arc::new(resolve_storage_config(id, config)?),
            );
        }

        Ok(Self { storage_runtimes })
    }

    pub fn get_storage_runtime(&self, id: &str) -> Option<Arc<Box<dyn StorageRuntime>>> {
        self.storage_runtimes.get(id).map(Arc::clone)
    }
}

#[async_trait]
pub trait StorageRuntime: Send + Sync + 'static {
    fn identifier(&self) -> &str;
    async fn get(&self, location: &Path) -> Result<(String, Option<String>), StorageError>;
    async fn get_if_none_changed(
        &self,
        location: &Path,
        if_none_match: Option<String>,
    ) -> Result<StorageGetResult, StorageError>;
}

pub enum StorageGetResult {
    NotModified,
    Modified {
        contents: String,
        etag: Option<String>,
    },
}

pub fn resolve_storage_config(
    id: &str,
    config: &StorageSourceConfig,
) -> Result<Box<dyn StorageRuntime>, StorageError> {
    match config {
        StorageSourceConfig::S3(s3_config) => {
            Ok(Box::new(s3_runtime::S3StorageRuntime::new(id, s3_config)?))
        }
    }
}
