use crate::database::DatabaseResult;
use crate::models::{
    Article, ArticleFilter, ArticleID, Category, CategoryID, CategoryMapping, DatabaseSize, Enclosure, FatArticle, FatFavIcon, FavIcon, Feed,
    FeedCount, FeedID, FeedMapping, FeedUpdateResult, ImageMetadata, Marked, OfflineAction, Read, SyncResult, Tag, TagID, Tagging, Thumbnail, Url,
};
use chrono::Duration;
#[cfg(test)]
use mockall::{automock, predicate::*};
use std::collections::HashMap;

#[cfg_attr(test, automock)]
pub trait DatabaseExt: Send + Sync {
    fn reset(&self) -> DatabaseResult<()>;
    fn clean(&self) -> DatabaseResult<()>;
    fn is_empty(&self) -> DatabaseResult<bool>;
    fn size(&self) -> DatabaseResult<DatabaseSize>;

    fn insert_tag(&self, tag: &Tag) -> DatabaseResult<()>;
    fn insert_tags(&self, tags: &[Tag]) -> DatabaseResult<()>;
    fn read_tags(&self) -> DatabaseResult<Vec<Tag>>;
    fn read_tags_for_article(&self, article_id: &ArticleID) -> DatabaseResult<Vec<Tag>>;
    fn drop_tag(&self, tag_id: &TagID) -> DatabaseResult<()>;
    fn set_tag_read(&self, tags: &[TagID]) -> DatabaseResult<()>;
    fn insert_tagging(&self, tagging: &Tagging) -> DatabaseResult<()>;
    fn insert_taggings(&self, taggings: &[Tagging]) -> DatabaseResult<()>;
    fn read_taggings<'a>(&self, article: Option<&'a ArticleID>, tag: Option<&'a TagID>) -> DatabaseResult<Vec<Tagging>>;
    fn drop_tagging(&self, tagging: &Tagging) -> DatabaseResult<()>;
    fn drop_taggings_of_tag(&self, tag: &Tag) -> DatabaseResult<()>;
    fn drop_taggings_of_article(&self, article: &ArticleID) -> DatabaseResult<()>;

    fn insert_category(&self, category: &Category) -> DatabaseResult<()>;
    fn insert_categories(&self, categories: &[Category]) -> DatabaseResult<()>;
    fn read_category(&self, category_id: &CategoryID) -> DatabaseResult<Category>;
    fn read_categories(&self) -> DatabaseResult<Vec<Category>>;
    fn drop_category(&self, category_id: &CategoryID) -> DatabaseResult<()>;
    fn set_category_read(&self, categories: &[CategoryID]) -> DatabaseResult<()>;

    fn insert_feed(&self, feed: &Feed) -> DatabaseResult<()>;
    fn insert_feeds(&self, feeds: &[Feed]) -> DatabaseResult<()>;
    fn read_feeds(&self) -> DatabaseResult<Vec<Feed>>;
    fn read_feed(&self, feed_id: &FeedID) -> DatabaseResult<Feed>;
    fn drop_feed(&self, feed_id: &FeedID) -> DatabaseResult<()>;
    fn set_feed_read(&self, feeds: &[FeedID]) -> DatabaseResult<()>;

    fn read_fatfavicon(&self, feed_id: &FeedID) -> DatabaseResult<FatFavIcon>;
    fn read_favicon(&self, feed_id: &FeedID) -> DatabaseResult<FavIcon>;
    fn insert_favicon(&self, favicon: &FatFavIcon) -> DatabaseResult<()>;
    fn insert_favicons(&self, favicons: &[FatFavIcon]) -> DatabaseResult<()>;
    fn drop_favicon(&self, feed_id: &FeedID) -> DatabaseResult<()>;

    fn read_thumbnail(&self, article_id: &ArticleID) -> DatabaseResult<Thumbnail>;
    fn insert_thumbnail(&self, thumbnail: &Thumbnail) -> DatabaseResult<()>;

    fn insert_feed_mapping(&self, mapping: &FeedMapping) -> DatabaseResult<()>;
    fn insert_feed_mappings(&self, mappings: &[FeedMapping]) -> DatabaseResult<()>;
    fn read_feed_mappings<'a>(&self, feed: Option<&'a FeedID>, category: Option<&'a CategoryID>) -> DatabaseResult<Vec<FeedMapping>>;
    fn drop_feed_mapping(&self, mapping: &FeedMapping) -> DatabaseResult<()>;
    fn drop_mapping_of_feed(&self, feed_id: &FeedID) -> DatabaseResult<()>;
    fn drop_feed_mappings_of_category(&self, category_id: &CategoryID) -> DatabaseResult<()>;

    fn insert_category_mapping(&self, mapping: &CategoryMapping) -> DatabaseResult<()>;
    fn insert_category_mappings(&self, mappings: &[CategoryMapping]) -> DatabaseResult<()>;
    fn read_category_mappings<'a>(&self, parent: Option<&'a CategoryID>, category: Option<&'a CategoryID>) -> DatabaseResult<Vec<CategoryMapping>>;
    fn drop_category_mapping(&self, mapping: &CategoryMapping) -> DatabaseResult<()>;
    fn drop_mapping_of_category(&self, category_id: &CategoryID) -> DatabaseResult<()>;

    fn update_articles_grabbed_content(&self, articles: &[FatArticle]) -> DatabaseResult<()>;
    fn update_article_grabbed_content(&self, article: &FatArticle) -> DatabaseResult<()>;
    fn write_articles(&self, articles: &[Article]) -> DatabaseResult<()>;
    fn read_fat_article(&self, id: &ArticleID) -> DatabaseResult<FatArticle>;
    fn read_fat_articles(&self, filter: ArticleFilter) -> DatabaseResult<Vec<FatArticle>>;
    fn read_article(&self, id: &ArticleID) -> DatabaseResult<Article>;
    fn read_articles(&self, filter: ArticleFilter) -> DatabaseResult<Vec<Article>>;
    fn article_exists(&self, article_id: &ArticleID) -> DatabaseResult<bool>;
    fn drop_articles(&self, articles: &[ArticleID]) -> DatabaseResult<()>;
    fn drop_old_articles(&self, older_than: Duration) -> DatabaseResult<()>;
    fn set_article_read(&self, articles: &[ArticleID], read: Read) -> DatabaseResult<()>;
    fn set_all_read(&self) -> DatabaseResult<()>;
    fn set_article_marked(&self, articles: &[ArticleID], marked: Marked) -> DatabaseResult<()>;

    fn read_enclosures(&self, article: &ArticleID) -> DatabaseResult<Vec<Enclosure>>;
    fn read_enclosure(&self, article: &ArticleID, url: &Url) -> DatabaseResult<Enclosure>;
    fn write_enclosures(&self, enclosures: &[Enclosure]) -> DatabaseResult<()>;
    fn drop_enclosures_of_article(&self, article: &ArticleID) -> DatabaseResult<()>;
    fn drop_enclosures_of_articles(&self, articles: &[ArticleID]) -> DatabaseResult<()>;

    fn unread_count_feed_map(&self, exclude_future: bool) -> DatabaseResult<HashMap<FeedID, i64>>;
    fn marked_count_feed_map(&self) -> DatabaseResult<Vec<FeedCount>>;
    fn today_unread_count(&self, exclude_future: bool) -> DatabaseResult<i64>;
    fn today_marked_count(&self) -> DatabaseResult<i64>;
    fn unread_count_all(&self) -> DatabaseResult<i64>;

    fn write_sync_result(&self, result: SyncResult, delete_articles_older_than: Option<Duration>) -> DatabaseResult<HashMap<FeedID, i64>>;
    fn write_feed_update_result(&self, feed_id: &FeedID, result: FeedUpdateResult) -> DatabaseResult<i64>;
    fn sort_alphabetically(&self) -> DatabaseResult<()>;

    fn insert_offline_actions(&self, offline_actions: &[OfflineAction]) -> DatabaseResult<()>;
    fn read_offline_actions(&self) -> DatabaseResult<Vec<OfflineAction>>;
    fn drop_offline_actions(&self) -> DatabaseResult<()>;

    fn read_image(&self, url: &Url) -> DatabaseResult<ImageMetadata>;
    fn read_images(&self) -> DatabaseResult<Vec<ImageMetadata>>;
    fn drop_all_images(&self) -> DatabaseResult<()>;
    fn write_image(&self, image: &ImageMetadata) -> DatabaseResult<()>;
}
