use std::collections::HashSet;
use std::path::PathBuf;

use async_trait::async_trait;

use crate::cli::Source;
use crate::constants::ELASTIC_FLICKR_PHOTOS_INDEX;
use crate::download::downloader::Downloader;
use crate::download::flickr_downloader::FlickrDownloader;
use crate::errors::Error;
use crate::helpers::es_helper::ElasticHelper;
use crate::models::flickr_photo::FlickrPhoto;
use crate::processors::processor::Processor;
use crate::processors::report::Report;

pub(crate) struct DownloadProcessor {
    source: Source,
    to: PathBuf,
    server: Option<String>,
    es_helper: ElasticHelper,
}

impl DownloadProcessor {
    pub fn new(source: Source, to: PathBuf, server: Option<String>) -> Self {
        DownloadProcessor {
            source,
            to,
            server,
            es_helper: ElasticHelper::new(ELASTIC_FLICKR_PHOTOS_INDEX),
        }
    }

    pub async fn execute_flickr_download(&self, downloader: FlickrDownloader) -> Result<Report, Vec<String>> {
        match &self.server {
            None => match self.full_flickr_download(downloader).await {
                Ok(n) => Ok(n),
                Err(_) => Err(vec![String::from("error occurs during full flickr download")])
            },
            Some(server) => match self.sample_flickr_download(downloader, server).await {
                Ok(n) => Ok(n),
                Err(_) => Err(vec![String::from("error occurs during full flickr download")])
            },
        }
    }

    #[allow(dead_code)]
    pub async fn full_flickr_download(&self, downloader: FlickrDownloader) -> Result<Report, Error> {
        let photos = self.get_all_flickr_photo_to_download().await;
        downloader.download_photos(photos).await
    }

    pub async fn sample_flickr_download(&self, downloader: FlickrDownloader, server: &str) -> Result<Report, Error> {
        let photos = self.get_flickr_photo_from_server_to_download(server).await;
        downloader.download_photos(photos).await
    }

    #[allow(dead_code)]
    async fn get_all_flickr_photo_to_download(&self) -> HashSet<FlickrPhoto> {
        self.es_helper
            .get_flickr_photos_to_download()
            .await
            .expect("es encounters an error")
    }

    async fn get_flickr_photo_from_server_to_download(&self, server: &str) -> HashSet<FlickrPhoto> {
        self.es_helper
            .get_flickr_photos_to_download_by_server(server)
            .await
            .expect("es encounters an error")
    }
}

#[async_trait]
impl Processor for DownloadProcessor {
    async fn process(&self) -> Result<Report, Vec<String>> {
        if self.source.eq(&Source::local) {
            Err(vec![String::from("error during process : local is not an accepted source for the download process")])
        } else if self.source.eq(&Source::flickr) {
            let downloader = FlickrDownloader::new(self.to.clone().into_os_string().into_string().unwrap_or_default());
            self.execute_flickr_download(downloader).await
        } else {
            Err(vec![String::from("error during process : source unknown")])
        }
    }
}
