use std::sync::Arc;

use tici_common::config::S3Config;

use crate::{
    backend::BackendParseError,
    storage::{self, BackendStorage},
};

#[derive(Debug)]
pub struct GCSBackendOptions {
    // currently GCS backend does not have specific options
}

impl GCSBackendOptions {
    pub fn from_config(_config: &S3Config) -> Result<Self, BackendParseError> {
        Ok(Self {})
    }

    pub fn to_query_iter(&self) -> impl Iterator<Item = (&'static str, &str)> {
        // currently no specific options for GCS backend
        std::iter::empty()
    }
}

#[derive(Debug)]
pub struct GCSBackend {
    pub bucket: String,
    pub prefix: String,
}

impl GCSBackend {
    pub fn from_url(u: url::Url, raw_url: &str) -> Result<Self, BackendParseError> {
        let bucket = u.host_str().ok_or_else(|| {
            BackendParseError::InvalidUrl(format!(
                "Missing bucket in GCS Backend URL, url={raw_url}"
            ))
        })?;
        let prefix = u.path().trim_start_matches('/').to_string();
        Ok(Self {
            bucket: bucket.to_string(),
            prefix,
        })
    }

    fn to_storage_config(&self) -> S3Config {
        S3Config {
            endpoint: "https://storage.googleapis.com".to_string(),
            bucket: self.bucket.clone(),
            prefix: self.prefix.clone(),
            ..S3Config::default()
        }
    }

    pub async fn create_storage(
        &self,
        service: &'static str,
    ) -> storage::Result<Arc<dyn BackendStorage>> {
        let config = self.to_storage_config();
        storage::create_backend_storage(&config, service).await
    }
}

#[cfg(test)]
mod tests {
    use super::super::Backend;

    #[test]
    fn test_gcs_backend_parse() {
        let url = "gcs://test-bucket";
        let backend = Backend::parse(url).unwrap();
        match backend {
            Backend::GCS(gcs) => {
                assert_eq!(gcs.bucket, "test-bucket");
                assert_eq!(gcs.prefix, "");
            }
            _ => unreachable!("Expected GCS backend"),
        }

        let url = "gs://test-bucket";
        let backend = Backend::parse(url).unwrap();
        match backend {
            Backend::GCS(gcs) => {
                assert_eq!(gcs.bucket, "test-bucket");
                assert_eq!(gcs.prefix, "");
            }
            _ => unreachable!("Expected GCS backend"),
        }

        let url = "gcs://test-bucket/prefix";
        let backend = Backend::parse(url).unwrap();
        match backend {
            Backend::GCS(gcs) => {
                assert_eq!(gcs.bucket, "test-bucket");
                assert_eq!(gcs.prefix, "prefix");
            }
            _ => unreachable!("Expected GCS backend"),
        }

        let url = "gs://test-bucket/prefix";
        let backend = Backend::parse(url).unwrap();
        match backend {
            Backend::GCS(gcs) => {
                assert_eq!(gcs.bucket, "test-bucket");
                assert_eq!(gcs.prefix, "prefix");
            }
            _ => unreachable!("Expected GCS backend"),
        }
    }

    #[test]
    fn test_backend_parse_invalid() {
        for u in ["", "not-a-url", "gcs:///path/without/bucket"] {
            let result = Backend::parse(u);
            assert!(result.is_err());
        }
    }

    #[test]
    fn test_gcs_backend_to_storage_config() {
        let backend = Backend::parse("gs://gcs-bucket/prefix").unwrap();
        let gcs = match backend {
            Backend::GCS(gcs) => gcs,
            _ => unreachable!("Expected GCS backend"),
        };
        let config = gcs.to_storage_config();
        assert_eq!(config.bucket, "gcs-bucket");
        assert_eq!(config.prefix, "prefix");
        assert_eq!(config.endpoint, "https://storage.googleapis.com");
    }
}
