use tokio::sync::{RwLock, Semaphore};

use crate::config::ConfigProxy;
use crate::database::DatabaseExt;
use crate::feed_api::portal::{Portal, PortalResult};
use crate::models::{
    Article, ArticleFilter, ArticleID, Category, CategoryID, CategoryMapping, Feed, FeedID, FeedMapping, Headline, Tag, TagID, Tagging,
};
use std::sync::Arc;

pub struct DefaultPortal {
    db: Arc<Box<dyn DatabaseExt>>,
    config: Arc<RwLock<ConfigProxy>>,
    download_semaphore: Arc<Semaphore>,
}

unsafe impl Send for DefaultPortal {}
unsafe impl Sync for DefaultPortal {}

impl DefaultPortal {
    pub fn new(db: Arc<Box<dyn DatabaseExt>>, config: Arc<RwLock<ConfigProxy>>, download_semaphore: Arc<Semaphore>) -> DefaultPortal {
        DefaultPortal {
            db,
            config,
            download_semaphore,
        }
    }
}

impl Portal for DefaultPortal {
    fn get_headlines(&self, ids: &[ArticleID]) -> PortalResult<Vec<Headline>> {
        let articles = self.get_articles(ids)?;
        let headlines = articles.iter().map(Headline::from_article).collect();
        Ok(headlines)
    }

    fn get_article(&self, id: &ArticleID) -> PortalResult<Article> {
        let article = self.db.read_article(id)?;
        Ok(article)
    }

    fn get_articles(&self, ids: &[ArticleID]) -> PortalResult<Vec<Article>> {
        let articles = self.db.read_articles(ArticleFilter {
            limit: Some(ids.len() as i64),
            ids: Some(ids.into()),
            ..ArticleFilter::default()
        })?;
        Ok(articles)
    }

    fn get_article_exists(&self, id: &ArticleID) -> PortalResult<bool> {
        let exists = self.db.article_exists(id)?;
        Ok(exists)
    }

    fn get_article_ids_unread_feed(&self, feed_id: &FeedID) -> PortalResult<Vec<ArticleID>> {
        let articles = self.db.read_articles(ArticleFilter::feed_unread(feed_id))?;

        Ok(articles.iter().map(|article| article.article_id.clone()).collect())
    }

    fn get_article_ids_unread_category(&self, category_id: &CategoryID) -> PortalResult<Vec<ArticleID>> {
        let articles = self.db.read_articles(ArticleFilter::category_unread(category_id))?;

        Ok(articles.iter().map(|article| article.article_id.clone()).collect())
    }

    fn get_article_ids_unread_all(&self) -> PortalResult<Vec<ArticleID>> {
        let articles = self.db.read_articles(ArticleFilter::all_unread())?;

        Ok(articles.iter().map(|article| article.article_id.clone()).collect())
    }

    fn get_article_ids_marked_all(&self) -> PortalResult<Vec<ArticleID>> {
        let articles = self.db.read_articles(ArticleFilter::all_marked())?;

        Ok(articles.iter().map(|article| article.article_id.clone()).collect())
    }

    fn get_feeds(&self) -> PortalResult<Vec<Feed>> {
        let feeds = self.db.read_feeds()?;
        Ok(feeds)
    }

    fn get_categories(&self) -> PortalResult<Vec<Category>> {
        let categories = self.db.read_categories()?;
        Ok(categories)
    }

    fn get_feed_mappings(&self) -> PortalResult<Vec<FeedMapping>> {
        let mappings = self.db.read_feed_mappings(None, None)?;
        Ok(mappings)
    }

    fn get_category_mappings(&self) -> PortalResult<Vec<CategoryMapping>> {
        let mappings = self.db.read_category_mappings(None, None)?;
        Ok(mappings)
    }

    fn get_tags(&self) -> PortalResult<Vec<Tag>> {
        let tags = self.db.read_tags()?;
        Ok(tags)
    }

    fn get_taggings(&self, article_id: Option<&ArticleID>, tag_id: Option<&TagID>) -> PortalResult<Vec<Tagging>> {
        let taggings = self.db.read_taggings(article_id, tag_id)?;
        Ok(taggings)
    }

    fn get_config(&self) -> Arc<RwLock<ConfigProxy>> {
        self.config.clone()
    }

    fn get_download_semaphore(&self) -> Arc<Semaphore> {
        self.download_semaphore.clone()
    }
}
