use async_trait::async_trait;
pub mod error;
pub mod portal;

use crate::models::{
    ArticleID, Category, CategoryID, FavIcon, Feed, FeedID, LoginData, Marked, PluginCapabilities, PluginID, PluginInfo, Read, SyncResult, TagID, Url,
};
use chrono::{DateTime, Utc};
use failure::Fail;
use reqwest::Client;
use std::path::PathBuf;

pub use self::error::{FeedApiError, FeedApiErrorKind};
pub use self::portal::Portal;

pub type FeedApiResult<T> = Result<T, FeedApiError>;

pub trait ApiMetadata {
    fn id(&self) -> PluginID;
    fn info(&self) -> FeedApiResult<PluginInfo>;
    fn parse_error(&self, error: &dyn Fail) -> Option<String>;
    fn get_instance(&self, config: &PathBuf, portal: Box<dyn Portal>) -> FeedApiResult<Box<dyn FeedApi>>;
}

#[async_trait]
pub trait FeedApi: Send + Sync {
    fn features(&self) -> FeedApiResult<PluginCapabilities>;
    fn has_user_configured(&self) -> FeedApiResult<bool>;
    fn user_name(&self) -> Option<String>;
    fn get_login_data(&self) -> Option<LoginData>;
    async fn is_logged_in(&self, client: &Client) -> FeedApiResult<bool>;
    async fn login(&mut self, data: LoginData, client: &Client) -> FeedApiResult<()>;
    async fn logout(&mut self, client: &Client) -> FeedApiResult<()>;
    async fn initial_sync(&self, client: &Client) -> FeedApiResult<SyncResult>;
    async fn sync(&self, max_count: u32, last_sync: DateTime<Utc>, client: &Client) -> FeedApiResult<SyncResult>;
    async fn set_article_read(&self, articles: &[ArticleID], read: Read, client: &Client) -> FeedApiResult<()>;
    async fn set_article_marked(&self, articles: &[ArticleID], marked: Marked, client: &Client) -> FeedApiResult<()>;
    async fn set_feed_read(&self, feeds: &[FeedID], last_sync: DateTime<Utc>, client: &Client) -> FeedApiResult<()>;
    async fn set_category_read(&self, categories: &[CategoryID], last_sync: DateTime<Utc>, client: &Client) -> FeedApiResult<()>;
    async fn set_tag_read(&self, tags: &[TagID], last_sync: DateTime<Utc>, client: &Client) -> FeedApiResult<()>;
    async fn set_all_read(&self, last_sync: DateTime<Utc>, client: &Client) -> FeedApiResult<()>;
    async fn add_feed(
        &self,
        url: &Url,
        title: Option<String>,
        category: Option<CategoryID>,
        client: &Client,
    ) -> FeedApiResult<(Feed, Option<Category>)>;
    async fn remove_feed(&self, id: &FeedID, client: &Client) -> FeedApiResult<()>;
    async fn move_feed(&self, feed_id: &FeedID, from: &CategoryID, to: &CategoryID, client: &Client) -> FeedApiResult<()>;
    async fn rename_feed(&self, feed_id: &FeedID, new_title: &str, client: &Client) -> FeedApiResult<FeedID>;
    async fn add_category(&self, title: &str, parent: Option<&CategoryID>, client: &Client) -> FeedApiResult<CategoryID>;
    async fn remove_category(&self, id: &CategoryID, remove_children: bool, client: &Client) -> FeedApiResult<()>;
    async fn rename_category(&self, id: &CategoryID, new_title: &str, client: &Client) -> FeedApiResult<CategoryID>;
    async fn move_category(&self, id: &CategoryID, parent: &CategoryID, client: &Client) -> FeedApiResult<()>;
    async fn import_opml(&self, opml: &str, client: &Client) -> FeedApiResult<()>;
    async fn add_tag(&self, title: &str, client: &Client) -> FeedApiResult<TagID>;
    async fn remove_tag(&self, id: &TagID, client: &Client) -> FeedApiResult<()>;
    async fn rename_tag(&self, id: &TagID, new_title: &str, client: &Client) -> FeedApiResult<TagID>;
    async fn tag_article(&self, article_id: &ArticleID, tag_id: &TagID, client: &Client) -> FeedApiResult<()>;
    async fn untag_article(&self, article_id: &ArticleID, tag_id: &TagID, client: &Client) -> FeedApiResult<()>;
    async fn get_favicon(&self, feed_id: &FeedID, client: &Client) -> FeedApiResult<FavIcon>;
}
