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

use crate::types::instance::InstanceTicker;
use crate::types::note::PackNoteMaybeFull;
use magnetar_sdk_macros::pack;

#[derive(Clone, Debug, Default, Deserialize, Serialize, TS)]
#[ts(export)]
pub enum AvatarDecoration {
    #[default]
    None,
    CatEars,
}

#[derive(Clone, Debug, Default, Deserialize, Serialize, TS)]
#[ts(export)]
pub enum SpeechTransform {
    #[default]
    None,
    Cat,
}

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
pub struct ProfileField {
    pub name: String,
    pub value: String,
    pub value_mm: Option<MmXml>,
    pub verified_at: Option<DateTime<Utc>>,
}

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
pub struct UserBase {
    pub acct: String,
    pub username: String,
    pub display_name: String,
    pub display_name_mm: Option<MmXml>,
    pub host: Option<String>,
    pub speech_transform: SpeechTransform,
    pub created_at: DateTime<Utc>,
    pub avatar_url: String,
    pub avatar_blurhash: Option<String>,
    pub avatar_decoration: AvatarDecoration,
    pub is_admin: bool,
    pub is_moderator: bool,
    pub is_bot: bool,
    pub emojis: EmojiContext,
    pub instance: Option<InstanceTicker>,
}

pack!(PackUserBase, Required<Id> as id & Required<UserBase> as user);

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
pub struct MovedTo {
    pub moved_to_uri: String,
    pub username: String,
    pub host: String,
}

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
pub struct UserProfileExt {
    pub is_locked: bool,
    pub is_silenced: bool,
    pub is_suspended: bool,

    pub description: Option<String>,
    pub description_mm: Option<MmXml>,
    pub location: Option<String>,
    pub birthday: Option<DateTime<Utc>>,
    pub fields: Vec<ProfileField>,

    pub follower_count: Option<usize>,
    pub following_count: Option<usize>,
    pub note_count: Option<usize>,

    pub url: Option<String>,

    pub moved_to: Option<MovedTo>,
    pub also_known_as: Option<String>,

    pub banner_url: Option<String>,
    pub banner_blurhash: Option<String>,

    pub has_public_reactions: bool,
}

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
pub struct UserProfilePinsEx {
    pub pinned_notes: Vec<PackNoteMaybeFull>,
}

#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum UserRelationship {
    Follow,
    FollowRequest,
    Mute,
    Block,
    RenoteMute,
}

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
pub struct UserRelationExt {
    pub follows_you: bool,
    pub you_follow: bool,
    pub they_request_follow: bool,
    pub you_request_follow: bool,
    pub blocks_you: bool,
    pub you_block: bool,
    pub mute: bool,
    pub mute_renotes: bool,
}

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
pub struct UserAuthOverviewExt {
    pub has_two_factor_enabled: bool,
    pub has_passwordless_login: bool,
    pub has_security_keys: bool,
}

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
pub struct UserSelfExt {
    pub avatar_id: Option<String>,
    pub banner_id: Option<String>,
    pub email_announcements: bool,
    pub always_mark_sensitive: bool,
    pub reject_bot_follow_requests: bool,
    pub reject_crawlers: bool,
    pub reject_ai_training: bool,
    pub word_mutes: Vec<String>,
    pub instance_mutes: Vec<String>,
    pub notification_settings: NotificationSettings,
}

pack!(
    PackUserSelf,
    Required<Id> as id & Required<UserBase> as user & Required<UserSelfExt> as self_info
);

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
pub struct UserDetailExt {
    pub last_fetched_at: Option<DateTime<Utc>>,
    pub uri: Option<String>,
    pub updated_at: Option<DateTime<Utc>>,
}

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
pub struct SecurityKeyBase {
    pub name: String,
    pub last_used_at: Option<DateTime<Utc>>,
}

pack!(PackSecurityKeyBase, Required<Id> as id & Required<SecurityKeyBase> as key);

#[derive(Clone, Debug, Deserialize, Serialize, TS)]
#[ts(export)]
pub struct UserSecretsExt {
    pub email: Option<String>,
    pub email_verified: bool,
    pub security_keys: Vec<PackSecurityKeyBase>,
}

pack!(
    PackUserMaybeAll,
    Required<Id> as id
        & Required<UserBase> as user
        & Optional<UserProfileExt> as profile
        & Optional<UserProfilePinsEx> as pins
        & Optional<UserDetailExt> as detail
        & Optional<UserRelationExt> as relation
        & Optional<UserAuthOverviewExt> as auth
);

impl From<PackUserBase> for PackUserMaybeAll {
    fn from(value: PackUserBase) -> Self {
        Self::pack_from((
            value.id,
            value.user,
            Optional(None),
            Optional(None),
            Optional(None),
            Optional(None),
            Optional(None),
        ))
    }
}

pack!(
    PackUserSelfMaybeAll,
    Required<Id> as id
        & Required<UserBase> as user
        & Optional<UserProfileExt> as profile
        & Optional<UserProfilePinsEx> as pins
        & Optional<UserDetailExt> as detail
        & Optional<UserSecretsExt> as secrets
        & Optional<UserSelfExt> as self_detail
);

impl From<PackUserBase> for PackUserSelfMaybeAll {
    fn from(value: PackUserBase) -> Self {
        Self::pack_from((
            value.id,
            value.user,
            Optional(None),
            Optional(None),
            Optional(None),
            Optional(None),
            Optional(None),
        ))
    }
}
