use crate::types::emoji::EmojiContext;
use crate::types::user::PackUserBase;
use crate::types::{Id, MmXml};
use crate::{Optional, Packed, Required};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use ts_rs::TS;

use crate::types::drive::PackDriveFileBase;

use magnetar_sdk_macros::pack;

#[derive(Copy, Clone, Default, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
pub struct NoteListFilter {
    show_renotes: Option<bool>,
    show_replies: Option<bool>,
    show_files_only: Option<bool>,
    uncwed_sensitive: Option<bool>,
}

#[derive(Copy, Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
pub enum NoteVisibility {
    Public,
    Home,
    Followers,
    Direct,
}

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
pub struct PollChoice {
    pub title: String,
    pub votes_count: usize,
    pub voted: Option<bool>,
}

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
pub struct PollBase {
    pub expires_at: Option<DateTime<Utc>>,
    pub expired: bool,
    pub multiple_choice: bool,
    pub options: Vec<PollChoice>,
}

pack!(PackPollBase, Required<Id> as id & Required<PollBase> as poll);

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
pub struct NoteBase {
    pub created_at: DateTime<Utc>,
    pub updated_at: Option<DateTime<Utc>>,
    pub cw: Option<String>,
    pub cw_mm: Option<MmXml>,
    pub uri: Option<String>,
    pub url: Option<String>,
    pub text: String,
    pub text_mm: Option<MmXml>,
    pub visibility: NoteVisibility,
    pub user: Box<PackUserBase>,
    pub parent_note_id: Option<String>,
    pub renoted_note_id: Option<String>,
    pub reply_count: usize,
    pub renote_count: usize,
    pub mentions: Vec<String>,
    pub visible_user_ids: Option<Vec<String>>,
    pub hashtags: Vec<String>,
    pub reactions: Vec<ReactionPair>,
    pub local_only: bool,
    pub has_poll: bool,
    pub file_ids: Vec<String>,
    pub emojis: EmojiContext,
    pub is_quote: bool,
    pub is_renote: bool,
}

pack!(PackNoteBase, Required<Id> as id & Required<NoteBase> as note);

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
pub struct NoteSelfContextExt {
    pub self_renote_count: Option<usize>,
}

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
pub struct NoteAttachmentExt {
    pub poll: Option<PackPollBase>,
    pub attachments: Vec<PackDriveFileBase>,
}

pack!(
    PackNoteMaybeAttachments,
    Required<Id> as id & Required<NoteBase> as note & Optional<NoteSelfContextExt> as user_context & Optional<NoteAttachmentExt> as attachment
);

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
pub struct NoteDetailExt {
    pub parent_note: Option<Box<PackNoteMaybeFull>>,
    pub renoted_note: Option<Box<PackNoteMaybeFull>>,
}

pack!(
    PackNoteMaybeFull,
    Required<Id> as id & Required<NoteBase> as note & Optional<NoteSelfContextExt> as user_context & Optional<NoteAttachmentExt> as attachment & Optional<NoteDetailExt> as detail
);

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
#[repr(transparent)]
pub struct ReactionUnicode(pub String);

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
pub struct ReactionShortcode {
    pub name: String,
    pub host: Option<String>,
    pub url: String,
}

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
pub struct ReactionUnknown {
    pub raw: String,
}

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
#[serde(untagged)]
pub enum Reaction {
    Unicode(ReactionUnicode),
    Shortcode(ReactionShortcode),
    Unknown(ReactionUnknown),
}

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
#[serde(untagged)]
pub enum ReactionPair {
    WithoutContext(Reaction, usize),
    WithContext(Reaction, usize, bool),
}
