use magnetar_model::ck;
use magnetar_model::note_model::data::sub_interaction_renote;
use magnetar_sdk::types::note::{
    NoteDetailExt, NoteSelfContextExt, PackNoteMaybeFull, PackPollBase, ReactionPair,
};
use magnetar_sdk::types::user::PackUserBase;
use magnetar_sdk::types::{
    drive::PackDriveFileBase,
    emoji::EmojiContext,
    note::{NoteAttachmentExt, NoteBase, NoteVisibility},
    user::UserBase,
    MmXml,
};
use magnetar_sdk::{Packed, Required};

use crate::model::{PackType, PackingContext};

pub struct NoteBaseSource<'a> {
    pub note: &'a ck::note::Model,
    pub cw_mm: Option<&'a MmXml>,
    pub text_mm: Option<&'a MmXml>,
    pub reactions: &'a Vec<ReactionPair>,
    pub user: &'a UserBase,
    pub emoji_context: &'a EmojiContext,
}

impl PackType<NoteBaseSource<'_>> for NoteBase {
    fn extract(
        _context: &PackingContext,
        NoteBaseSource {
            note,
            text_mm,
            cw_mm,
            reactions,
            user,
            emoji_context,
        }: NoteBaseSource<'_>,
    ) -> Self {
        use ck::sea_orm_active_enums::NoteVisibilityEnum as NVE;
        NoteBase {
            created_at: note.created_at.into(),
            updated_at: note.updated_at.map(|d| d.into()),
            cw: note.cw.clone(),
            cw_mm: cw_mm.cloned(),
            uri: note.uri.clone(),
            url: note.url.clone(),
            text: note.text.clone().unwrap_or_default(),
            text_mm: text_mm.map(ToOwned::to_owned).clone(),
            visibility: match note.visibility {
                NVE::Followers => NoteVisibility::Followers,
                NVE::Hidden => NoteVisibility::Direct,
                NVE::Public => NoteVisibility::Public,
                NVE::Home => NoteVisibility::Home,
                NVE::Specified => NoteVisibility::Direct,
            },
            user: Box::new(PackUserBase::pack_from((
                Required(note.user_id.as_str().into()),
                Required(user.clone()),
            ))),
            parent_note_id: note.reply_id.clone(),
            renoted_note_id: note.renote_id.clone(),
            reply_count: note.replies_count as usize,
            renote_count: note.renote_count as usize,
            mentions: note.mentions.clone(),
            hashtags: note.tags.clone(),
            visible_user_ids: matches!(note.visibility, NVE::Specified)
                .then(|| note.visible_user_ids.clone()),
            reactions: reactions.clone(),
            local_only: note.local_only,
            has_poll: note.has_poll,
            file_ids: note.file_ids.clone(),
            emojis: emoji_context.clone(),
            is_quote: note.is_quote.unwrap_or_default(),
            is_renote: note.is_renote.unwrap_or_default(),
        }
    }
}

impl PackType<&sub_interaction_renote::Model> for NoteSelfContextExt {
    fn extract(_context: &PackingContext, data: &sub_interaction_renote::Model) -> Self {
        NoteSelfContextExt {
            self_renote_count: data.renotes.map(|v| v as usize),
        }
    }
}

pub struct NoteDetailSource<'a> {
    pub parent_note: Option<&'a PackNoteMaybeFull>,
    pub renoted_note: Option<&'a PackNoteMaybeFull>,
}

impl<'a> PackType<NoteDetailSource<'a>> for NoteDetailExt {
    fn extract(
        _context: &PackingContext,
        NoteDetailSource {
            parent_note,
            renoted_note,
        }: NoteDetailSource<'a>,
    ) -> Self {
        NoteDetailExt {
            parent_note: parent_note.cloned().map(Box::new),
            renoted_note: renoted_note.cloned().map(Box::new),
        }
    }
}

pub struct NoteAttachmentSource<'a> {
    pub attachments: &'a [PackDriveFileBase],
    pub poll: Option<&'a PackPollBase>,
}

impl<'a> PackType<NoteAttachmentSource<'a>> for NoteAttachmentExt {
    fn extract(
        _context: &PackingContext,
        NoteAttachmentSource { poll, attachments }: NoteAttachmentSource<'a>,
    ) -> Self {
        NoteAttachmentExt {
            attachments: attachments.to_vec(),
            poll: poll.cloned(),
        }
    }
}
