use std::sync::Arc;

use anyhow::Result;
use cloud::s3::S3Service;
use fts_index::storage::meta::StorageMeta;
use fts_index::{schema::*, storage::SchemaVersion};
use lazy_static::lazy_static;

lazy_static! {
    static ref INDEX_NAME_REGEX: regex::Regex =
        regex::Regex::new(r"^[a-z][a-z0-9_-]{0,254}$").unwrap();
}

fn is_valid_index_name(index_name: &str) -> bool {
    INDEX_NAME_REGEX.is_match(index_name)
}

pub(crate) async fn create_index(index_name: &str) -> Result<()> {
    if !is_valid_index_name(index_name) {
        return Err(anyhow::anyhow!("Invalid index name"));
    }
    let s3service = Arc::new(S3Service::default());
    let mut new_storage_meta = StorageMeta::default();
    let schema_version = SchemaVersion::from(SCHEMA_VERSION);

    // copy indexes from old storage_meta, schema_version must contains index.schema_version
    let storage_meta = StorageMeta::load_latest_cloud(s3service.clone())
        .await?
        .unwrap_or_default();
    for (i_name, index) in storage_meta.indexes.iter() {
        if i_name == index_name {
            if schema_version == index.schema_version {
                return Err(anyhow::anyhow!("Index schema-version already exists"));
            }
            let mut new_index = index.clone();
            new_index.schema_version = schema_version.clone();

            if schema_version.contains(&index.schema_version) {
                new_storage_meta
                    .indexes
                    .insert(index_name.to_string(), new_index);
            }
        } else {
            new_storage_meta
                .indexes
                .insert(i_name.clone(), index.clone());
        }
    }

    new_storage_meta.version = storage_meta.version + 1;
    new_storage_meta.upload(s3service).await?;

    println!(
        "Index created: {}, schema-version: {:?}, storage-meta-version: {:?}",
        index_name, schema_version, new_storage_meta.version
    );
    Ok(())
}
#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_is_valid_index_name() {
        assert!(is_valid_index_name("valid_index"));
        assert!(is_valid_index_name("valid-index_123"));
        assert!(!is_valid_index_name("InvalidIndex"));
        assert!(!is_valid_index_name("invalid index"));
        assert!(!is_valid_index_name("invalid-index!"));
        assert!(!is_valid_index_name(""));
        assert!(!is_valid_index_name("a".repeat(256).as_str()));
    }

    #[tokio::test]
    async fn test_create_index_valid_name() -> Result<()> {
        let s3service = Arc::new(S3Service::default());
        let latest_storage_meta1 = StorageMeta::load_latest_cloud(s3service.clone())
            .await?
            .unwrap_or_default();

        let result = create_index("valid_index").await;
        assert!(result.is_ok());

        let latest_storage_meta2 = StorageMeta::load_latest_cloud(s3service.clone())
            .await?
            .unwrap_or_default();

        assert!(latest_storage_meta1.version < latest_storage_meta2.version);

        //cleanup
        latest_storage_meta2.delete_from_cloud(s3service).await?;
        Ok(())
    }
}
