use crate::{CalckeyDbError, CalckeyModel};
use ck::emoji;
use sea_orm::prelude::Expr;
use sea_orm::{ColumnTrait, EntityTrait, IntoSimpleExpr, QueryFilter, QueryOrder};

#[derive(Debug, Copy, Clone)]
pub struct EmojiTag<'a> {
    pub name: &'a str,
    pub host: Option<&'a str>,
}

pub struct EmojiResolver(CalckeyModel);

impl EmojiResolver {
    pub fn new(db: CalckeyModel) -> Self {
        EmojiResolver(db)
    }

    pub async fn get_local_emoji(&self) -> Result<Vec<emoji::Model>, CalckeyDbError> {
        Ok(emoji::Entity::find()
            .filter(emoji::Column::Host.is_null())
            .order_by_asc(emoji::Column::Category)
            .order_by_asc(emoji::Column::Name)
            .all(self.0.inner())
            .await?)
    }

    pub async fn fetch_emoji(
        &self,
        shortcode: &str,
        host: Option<&str>,
    ) -> Result<Option<emoji::Model>, CalckeyDbError> {
        let host_filter = if let Some(host) = host {
            emoji::Column::Host.eq(host)
        } else {
            emoji::Column::Host.is_null()
        };

        let name_filter = emoji::Column::Name.eq(shortcode);
        let filter = host_filter.and(name_filter);

        Ok(emoji::Entity::find()
            .filter(filter)
            .one(self.0.inner())
            .await?)
    }

    pub async fn fetch_many_emojis(
        &self,
        shortcodes: &[String],
        host: Option<&str>,
    ) -> Result<Vec<emoji::Model>, CalckeyDbError> {
        let host_filter = if let Some(host) = host {
            emoji::Column::Host.eq(host)
        } else {
            emoji::Column::Host.is_null()
        };

        let name_filter = emoji::Column::Name.is_in(shortcodes);
        let filter = host_filter.and(name_filter);

        Ok(emoji::Entity::find()
            .filter(filter)
            .all(self.0.inner())
            .await?)
    }

    pub async fn fetch_many_tagged_emojis(
        &self,
        shortcodes: &[EmojiTag<'_>],
    ) -> Result<Vec<emoji::Model>, CalckeyDbError> {
        let remote_shortcode_host_pairs = shortcodes
            .iter()
            .filter_map(|s| {
                s.host.map(|host| {
                    Expr::tuple([
                        Expr::value(s.name.to_string()),
                        Expr::value(host.to_string()),
                    ])
                })
            })
            .collect::<Vec<_>>();
        let local_shortcodes = shortcodes
            .iter()
            .filter_map(|s| s.host.is_none().then_some(s.name.to_string()))
            .collect::<Vec<_>>();

        let remote_filter = Expr::tuple([
            emoji::Column::Name.into_simple_expr(),
            emoji::Column::Host.into_simple_expr(),
        ])
        .is_in(remote_shortcode_host_pairs);
        let local_filter = emoji::Column::Name
            .is_in(local_shortcodes)
            .and(emoji::Column::Host.is_null());
        let filter = remote_filter.or(local_filter);

        Ok(emoji::Entity::find()
            .filter(filter)
            .all(self.0.inner())
            .await?)
    }
}
