use super::entity::{TvShow, TvShowSource};
use crate::language::Language;
use crate::tvshow::entity::*;

#[derive(Debug, thiserror::Error)]
pub enum SubscribeError {
    #[error("tvshow now found")]
    NotFound,
    #[error(transparent)]
    Internal(#[from] anyhow::Error),
}

#[cfg_attr(any(test, feature = "mocks"), auto_impl::auto_impl(Arc))]
#[cfg_attr(any(test, feature = "mocks"), mockall::automock)]
pub trait TvShowService: Send + Sync + 'static {
    fn find_tvshow_by_id(
        &self,
        user_id: Option<u64>,
        tvshow_id: u64,
        language: Language,
    ) -> impl Future<Output = anyhow::Result<Option<FindTvShowResponse>>> + Send;

    fn find_tvshow_episode_by_id(
        &self,
        user_id: Option<u64>,
        episode_id: u64,
        language: Language,
    ) -> impl Future<Output = anyhow::Result<Option<FindTvShowEpisodeResponse>>> + Send;

    fn find_tvshow_season_by_id(
        &self,
        user_id: Option<u64>,
        season_id: u64,
        language: Language,
    ) -> impl Future<Output = anyhow::Result<Option<FindTvShowSeasonResponse>>> + Send;

    fn list_tvshows<'a>(
        &self,
        params: ListTvShowParams<'a>,
    ) -> impl Future<Output = anyhow::Result<ListTvShowResponse>> + Send;

    fn list_tvshow_episodes<'a>(
        &self,
        params: ListTvShowEpisodeParams<'a>,
    ) -> impl Future<Output = anyhow::Result<ListTvShowEpisodeResponse>> + Send;

    fn list_tvshow_seasons<'a>(
        &self,
        params: ListTvShowSeasonParams<'a>,
    ) -> impl Future<Output = anyhow::Result<ListTvShowSeasonResponse>> + Send;

    fn upsert_tvshow_episode_progressions(
        &self,
        input: &[TvShowEpisodeProgressInput],
    ) -> impl Future<Output = anyhow::Result<Vec<TvShowEpisodeProgress>>> + Send;

    fn subscribe(
        &self,
        user_id: u64,
        origin: TvShowSource,
        language: Language,
    ) -> impl Future<Output = Result<(), SubscribeError>> + Send;

    fn unsubscribe(
        &self,
        user_id: u64,
        tvshow_id: u64,
    ) -> impl Future<Output = anyhow::Result<()>> + Send;

    fn request_synchronize_all(
        &self,
        user_id: Option<u64>,
    ) -> impl Future<Output = anyhow::Result<()>> + Send;

    fn request_synchronize(
        &self,
        user_id: Option<u64>,
        tvshow_id: u64,
    ) -> impl Future<Output = anyhow::Result<()>> + Send;

    fn request_synchronize_located(
        &self,
        user_id: Option<u64>,
        tvshow_id: u64,
        language: Language,
    ) -> impl Future<Output = anyhow::Result<()>> + Send;

    fn synchronize(
        &self,
        user_id: Option<u64>,
        tvshow_id: u64,
    ) -> impl Future<Output = anyhow::Result<()>> + Send;

    fn synchronize_all(
        &self,
        user_id: Option<u64>,
    ) -> impl Future<Output = anyhow::Result<()>> + Send;

    fn synchronize_located(
        &self,
        user_id: Option<u64>,
        tvshow_id: u64,
        language: Language,
    ) -> impl Future<Output = anyhow::Result<()>> + Send;

    fn synchronize_season_located(
        &self,
        user_id: Option<u64>,
        params: SynchronizeSeasonParams,
    ) -> impl Future<Output = anyhow::Result<()>> + Send;
}

#[cfg_attr(any(test, feature = "mocks"), auto_impl::auto_impl(Arc))]
#[cfg_attr(any(test, feature = "mocks"), mockall::automock)]
pub trait TvShowProvider: Send + Sync + 'static {
    fn find_by_source(
        &self,
        source: TvShowSource,
        language: Language,
    ) -> impl Future<Output = anyhow::Result<Option<ExternalTvShow>>> + Send;
    fn find_season_by_source(
        &self,
        source: TvShowSource,
        language: Language,
        season_number: u64,
    ) -> impl Future<Output = anyhow::Result<Option<ExternalTvShowSeason>>> + Send;
}

#[cfg_attr(any(test, feature = "mocks"), auto_impl::auto_impl(Arc))]
#[cfg_attr(any(test, feature = "mocks"), mockall::automock)]
pub trait TvShowRepository: Send + Sync + 'static {
    fn find_by_source(
        &self,
        source: TvShowSource,
        language: Language,
    ) -> impl Future<Output = anyhow::Result<Option<TvShow>>> + Send;

    fn find_by_id(
        &self,
        tvshow_id: u64,
        language: Language,
    ) -> impl Future<Output = anyhow::Result<Option<TvShow>>> + Send;

    fn list<'a>(
        &self,
        params: ListTvShowParams<'a>,
    ) -> impl Future<Output = anyhow::Result<Vec<TvShow>>> + Send;

    fn list_languages(
        &self,
        tvshow_ids: &[u64],
    ) -> impl Future<Output = anyhow::Result<Vec<(u64, Language)>>> + Send;

    fn upsert(&self, input: &ExternalTvShow)
    -> impl Future<Output = anyhow::Result<TvShow>> + Send;
}

#[cfg_attr(any(test, feature = "mocks"), auto_impl::auto_impl(Arc))]
#[cfg_attr(any(test, feature = "mocks"), mockall::automock)]
pub trait TvShowEpisodeRepository: Send + Sync + 'static {
    fn find_by_id(
        &self,
        episode_id: u64,
        language: Language,
    ) -> impl Future<Output = anyhow::Result<Option<TvShowEpisode>>> + Send;
    fn list<'a>(
        &self,
        params: ListTvShowEpisodeParams<'a>,
    ) -> impl Future<Output = anyhow::Result<Vec<TvShowEpisode>>> + Send;
    fn upsert(
        &self,
        season_id: u64,
        input: &[ExternalTvShowEpisode],
    ) -> impl Future<Output = anyhow::Result<Vec<TvShowEpisode>>> + Send;
    fn list_progressions(
        &self,
        user_id: u64,
        episode_ids: &[u64],
    ) -> impl Future<Output = anyhow::Result<Vec<TvShowEpisodeProgress>>> + Send;
    fn delete_progressions(
        &self,
        user_id: u64,
        episode_ids: &[u64],
    ) -> impl Future<Output = anyhow::Result<()>> + Send;
    fn upsert_progressions(
        &self,
        inputs: &[TvShowEpisodeProgressInput],
    ) -> impl Future<Output = anyhow::Result<Vec<TvShowEpisodeProgress>>> + Send;
}

#[cfg_attr(any(test, feature = "mocks"), auto_impl::auto_impl(Arc))]
#[cfg_attr(any(test, feature = "mocks"), mockall::automock)]
pub trait TvShowSeasonRepository: Send + Sync + 'static {
    fn find_by_id(
        &self,
        season_id: u64,
        language: Language,
    ) -> impl Future<Output = anyhow::Result<Option<TvShowSeason>>> + Send;
    fn list<'a>(
        &self,
        params: ListTvShowSeasonParams<'a>,
    ) -> impl Future<Output = anyhow::Result<Vec<TvShowSeason>>> + Send;
    fn upsert(
        &self,
        tvshow_id: u64,
        input: &ExternalTvShowSeason,
    ) -> impl Future<Output = anyhow::Result<TvShowSeason>> + Send;
}

#[cfg_attr(any(test, feature = "mocks"), auto_impl::auto_impl(Arc))]
#[cfg_attr(any(test, feature = "mocks"), mockall::automock)]
pub trait TvShowSubscriptionRepository: Send + Sync + 'static {
    fn list_by_ids(
        &self,
        user_id: u64,
        tvshow_ids: &[u64],
    ) -> impl Future<Output = anyhow::Result<Vec<TvShowSubscription>>> + Send;
    fn create(
        &self,
        user_id: u64,
        tvshow_id: u64,
    ) -> impl Future<Output = anyhow::Result<()>> + Send;
    fn delete(
        &self,
        user_id: u64,
        tvshow_id: u64,
    ) -> impl Future<Output = anyhow::Result<()>> + Send;
    fn list(
        &self,
        user_id: u64,
        language: Language,
    ) -> impl Future<Output = anyhow::Result<Vec<TvShowWithSubscription>>> + Send;
}

#[cfg_attr(any(test, feature = "mocks"), auto_impl::auto_impl(Arc))]
#[cfg_attr(any(test, feature = "mocks"), mockall::automock)]
pub trait TvShowEpisodeFileRepository: Send + Sync + 'static {
    fn list<'a>(
        &self,
        params: ListEpisodeFileParams<'a>,
    ) -> impl Future<Output = anyhow::Result<Vec<TvShowEpisodeFile>>> + Send;
}
