#![warn(missing_docs, non_ascii_idents, trivial_numeric_casts,
    unused_crate_dependencies, noop_method_call, single_use_lifetimes, trivial_casts,
    unused_lifetimes, nonstandard_style, variant_size_differences)]
#![deny(keyword_idents)]
#![allow(clippy::needless_return)]
mod types;
mod connection;
mod modules;
mod models;
use std::sync::Arc;
use connection::Connection;
use modules::file::File;
use modules::help::Help;
use modules::search::Search;
use modules::ingest::Ingest;
use modules::submit::Submit;
pub use types::{Authentication, Error, Sha256, JsonMap};
pub struct Client {
    pub file: File,
    pub help: Help,
    pub ingest: Ingest,
    pub search: Search,
    pub submit: Submit,
    }
impl Client {
    pub async fn connect(server: String, auth: Authentication) -> Result<Self, Error> {
        let connection = Arc::new(Connection::connect(server, auth, None, true, Default::default(), None, None).await?);
        Ok(Self {
            file: File::new(connection.clone()),
            help: Help::new(connection.clone()),
            ingest: Ingest::new(connection.clone()),
            search: Search::new(connection.clone()),
            submit: Submit::new(connection)
            })
    }
}
#[cfg(test)]
mod tests {
    use rand::{thread_rng, Rng};
    use crate::models::submission::{SubmissionState, SubmissionParams, ServiceSelection};
    use crate::{Authentication, Client};
    fn init() {
        let _ = env_logger::builder().is_test(true).try_init();
    }
    pub (crate) async fn prepare_client() -> Client {
        init();
        let url = std::env::var("ASSEMBLYLINE_URL").unwrap();
        let username = std::env::var("ASSEMBLYLINE_USER").unwrap();
        let key = std::env::var("ASSEMBLYLINE_KEY").unwrap();
        Client::connect(url, Authentication::ApiKey { username, key }).await.unwrap()
    }
    fn random_body() -> Vec<u8> {
        let mut out = vec![];
        let mut prng = thread_rng();
        let length = 128 + prng.gen_range(0..256);
        while out.len() < length {
            out.push(prng.gen());
        }
        out
    }
    #[tokio::test]
    async fn submit_content() {
        let client = prepare_client().await;
        let result = client.submit.single()
            .metadata_item("testbatch".to_owned(), "0".to_owned())
            .params(SubmissionParams{ ttl: 1, ..Default::default()})
            .fname("test-file".to_owned())
            .content(random_body()).await.unwrap();
        assert_eq!(result.state, SubmissionState::Submitted);
    }
    #[tokio::test]
    async fn search_single_page() {
        let client = prepare_client().await;
        let batch: u64 = thread_rng().gen();
        let batch: String = batch.to_string();
        let _result = client.ingest.single()
            .metadata_item("testbatch".to_owned(), batch.clone())
            .notification_queue(batch.clone())
            .params(SubmissionParams{ priority: 300, ttl: 1, services: ServiceSelection{ selected: Some(vec!["Characterize".to_owned()]), ..Default::default()}, ..Default::default()})
            .fname("test-file".to_owned())
            .content(random_body()).await.unwrap();
        for _ in 0..100 {
            match client.ingest.get_message(&batch).await.unwrap() {
                Some(message) => {
                    assert_eq!(message.submission.metadata.get("testbatch").unwrap(), &batch);
                    break;
                },
                None => { tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; },
            }
        }
        for _ in 0..10 {
            let result = client.search.submission(format!("metadata.testbatch: {batch}"))
                .search().await.unwrap();
            if result.items.len() == 1 {
                return
            }
            tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
        }
        panic!()
    }
}