use minio::s3::{
    args::{BucketExistsArgs, MakeBucketArgs}, client::ClientBuilder, creds::StaticProvider, error::Error, http::BaseUrl, types::S3Api, Client
};
use tracing::{instrument, trace};
use url::Url;

use crate::Config;

pub struct S3 {
    bucket_name: String,
    client: Client,
}

impl S3 {
    #[instrument(skip_all, name = "S3")]
    pub async fn connect(config: &Config<'_>) -> Result<Self, Error> {
        let base_url = config.s3_url.parse::<BaseUrl>().unwrap();

        let static_provider =
            StaticProvider::new(&config.s3_access_key, &config.s3_secret_key, None);

        let client = ClientBuilder::new(base_url)
            .provider(Some(Box::new(static_provider)))
            .build()?;

        trace!("Checking bucket...");
        let exists = client
            .bucket_exists(&BucketExistsArgs::new(&config.s3_bucket).unwrap())
            .await?;

        if !exists {
            trace!("Creating bucket...");
            client
                .make_bucket(&MakeBucketArgs::new(&config.s3_bucket).unwrap())
                .await?;
        }

        trace!("Connection successfull");

        Ok(Self {
            bucket_name: config.s3_bucket.to_owned(),
            client: client,
        })
    }

    pub async fn store(&self, data: &str, name: &Url) {
        if let Some(domain) = name.domain() {
            let filename = domain.to_string() + name.path();

            let _ = &self
                .client
                .put_object_content(&self.bucket_name, &filename, data.to_owned())
                .send()
                .await
                .unwrap();
        }
    }
    
    pub async fn _get(&self, name: &Url) -> Option<String> {
         if let Some(domain) = name.domain() {
            let filename = domain.to_string() + name.path();

            let data = self
                .client
                .get_object(&self.bucket_name, &filename)
                .send()
                .await
                .unwrap();
             
            if let Ok(segments )= data.content.to_segmented_bytes().await {
                return Some(segments.to_bytes().iter().map(|c| *c as char).collect::<String>())
            }
        }
        None
    }
}
