use chrono::NaiveDateTime;
use serde::{Deserialize, Serialize};
use solana_sdk::pubkey::Pubkey;

#[cfg(feature = "redis")]
use redis_derive::{FromRedisValue, ToRedisArgs};

/// Response for a successful login, containing the JWT.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AuthResponse {
    pub token: String,
}

/// Response for a successful supply endpoint.
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
pub struct SupplyResponse {
    pub result: String,
}

/// Response after successfully sending a chat message.
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
pub struct ChatSendMessageResponse {
    pub status: String,
    pub message: String,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct User {
    pub authority: String,
    pub username: String,
    pub profile_photo_url: Option<String>,
    pub discord_user: Option<DiscordUser>,
    pub updated_at: NaiveDateTime,
    pub risk_score: i64,
    pub is_banned: bool,
    pub role: Option<String>,
}

// Notifications
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "redis", derive(FromRedisValue, ToRedisArgs))]
pub struct ChatNotification {
    pub authority: String,
    pub username: String,
    pub text: String,
    pub id: u64,
    pub ts: i64,
    pub profile_photo_url: Option<String>,
    pub role: Option<String>,
    pub discord_user_id: Option<String>,
    // Reply fields (optional - null if not a reply)
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub reply_to_id: Option<u64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub reply_to_text: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub reply_to_username: Option<String>,
    // Reaction counts (optional - for backwards compatibility)
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub reactions: Option<ChatReactions>,
}

/// Reaction counts for a chat message.
/// Uses named fields instead of emoji keys for type safety and Redis compatibility.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
#[cfg_attr(feature = "redis", derive(FromRedisValue, ToRedisArgs))]
pub struct ChatReactions {
    #[serde(default, skip_serializing_if = "is_zero")]
    pub thumbs_up: u32, // 👍
    #[serde(default, skip_serializing_if = "is_zero")]
    pub heart: u32, // ❤️
    #[serde(default, skip_serializing_if = "is_zero")]
    pub laughing: u32, // 😂
    #[serde(default, skip_serializing_if = "is_zero")]
    pub surprised: u32, // 😮
    #[serde(default, skip_serializing_if = "is_zero")]
    pub sad: u32, // 😢
    #[serde(default, skip_serializing_if = "is_zero")]
    pub fire: u32, // 🔥
}

fn is_zero(n: &u32) -> bool {
    *n == 0
}

impl ChatReactions {
    /// Check if all reaction counts are zero.
    pub fn is_empty(&self) -> bool {
        self.thumbs_up == 0
            && self.heart == 0
            && self.laughing == 0
            && self.surprised == 0
            && self.sad == 0
            && self.fire == 0
    }

    /// Get the count for a specific emoji.
    pub fn get(&self, emoji: &str) -> u32 {
        match emoji {
            "👍" => self.thumbs_up,
            "❤️" | "❤" => self.heart,
            "😂" => self.laughing,
            "😮" => self.surprised,
            "😢" => self.sad,
            "🔥" => self.fire,
            _ => 0,
        }
    }

    /// Set the count for a specific emoji.
    pub fn set(&mut self, emoji: &str, count: u32) {
        match emoji {
            "👍" => self.thumbs_up = count,
            "❤️" | "❤" => self.heart = count,
            "😂" => self.laughing = count,
            "😮" => self.surprised = count,
            "😢" => self.sad = count,
            "🔥" => self.fire = count,
            _ => {}
        }
    }
}

/// Allowed reaction emoji.
pub const ALLOWED_REACTION_EMOJI: [&str; 6] = ["👍", "❤️", "😂", "😮", "😢", "🔥"];

/// Check if an emoji is in the allowed reaction set.
pub fn is_valid_reaction_emoji(emoji: &str) -> bool {
    // Handle heart emoji variant (with and without variation selector)
    if emoji == "❤" {
        return true;
    }
    ALLOWED_REACTION_EMOJI.contains(&emoji)
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct ResetNotification {
    pub block_id: u64,
}

/// Notification for a deploy event, containing all relevant data for clients.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "redis", derive(FromRedisValue, ToRedisArgs))]
pub struct DeployNotification {
    /// The authority (miner) who deployed.
    pub authority: String,
    /// The amount of SOL deployed (in lamports).
    pub amount: u64,
    /// The mask (bitmask of selected squares).
    pub mask: u64,
    /// The round ID this deployment is for.
    pub round_id: u64,
    /// The signer of the transaction.
    pub signer: String,
    /// The strategy used for deployment.
    pub strategy: u64,
    /// Total squares selected.
    pub total_squares: u64,
    /// Timestamp of the deployment.
    pub ts: i64,
    /// Transaction signature.
    pub sig: String,
}

/// Notification for a reaction being added or removed from a chat message.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct ReactionNotification {
    pub message_id: u64,
    pub emoji: String,
    pub count: u32,
    pub action: String, // "added" or "removed"
}

/// A user currently typing in chat.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct TypingUser {
    pub authority: String,
    pub username: String,
}

/// Notification for typing indicator updates.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct TypingNotification {
    pub users: Vec<TypingUser>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum Notification {
    Chat(ChatNotification),
    Reset(ResetNotification),
    Deploy(DeployNotification),
    Reaction(ReactionNotification),
    Typing(TypingNotification),
}

impl Notification {
    pub fn id(&self) -> String {
        match self {
            Notification::Chat(chat) => chat.id.to_string(),
            Notification::Reset(reset) => reset.block_id.to_string(),
            Notification::Deploy(deploy) => deploy.sig.clone(),
            Notification::Reaction(reaction) => {
                format!("{}:{}", reaction.message_id, reaction.emoji)
            }
            Notification::Typing(_) => "typing".to_string(),
        }
    }
}

/// Response after adding/removing a reaction.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct ChatReactResponse {
    pub status: String,
    pub action: String,     // "added" or "removed"
    pub message_id: u64,
    pub emoji: String,
    pub count: u32,         // new count for this emoji on this message
}

/// Response for typing indicator endpoint.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct ChatTypingResponse {
    pub status: String,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct DailyRevenue {
    pub day: String,
    pub revenue: i64,
}

// Username validation

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct UsernameValidationResponse {
    pub valid: bool,
    pub error: Option<String>,
}

impl UsernameValidationResponse {
    pub fn valid() -> Self {
        Self {
            valid: true,
            error: None,
        }
    }

    pub fn invalid(error: String) -> Self {
        Self {
            valid: false,
            error: Some(error),
        }
    }
}

/// Response for a successful Discord authentication.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct DiscordAuthResponse {
    pub access_token: String,
}

/// Google user information returned after authentication.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GoogleUser {
    pub email: String,
    pub name: String,
    pub picture: Option<String>,
}

/// Response for a successful Google authentication.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GoogleAuthResponse {
    pub jwt: String,
    pub user: GoogleUser,
}

#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
pub struct DiscordUser {
    pub id: String,
    pub username: String,
    pub discriminator: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub global_name: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub avatar: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub verified: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub email: Option<String>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct OreBalance {
    pub wallet: u64,
    pub staked: u64,
    pub unrefined: u64,
    pub refined: u64,
    pub lifetime_deployed_sol: u64,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct LeaderboardEntry {
    pub authority: String,
    pub amount: u64,
    pub username: Option<String>,
    pub profile_picture_url: Option<String>,
}

/// Response type for reset events with enriched top miner user info.
/// Maintains field order matching ore_api::event::ResetEvent for backwards compatibility.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct ResetEventResponse {
    pub disc: u8,
    pub round_id: u64,
    pub start_slot: u64,
    pub end_slot: u64,
    pub winning_square: u64,
    pub top_miner: Pubkey,
    pub num_winners: u64,
    pub motherlode: u64,
    pub total_deployed: u64,
    pub total_vaulted: u64,
    pub total_winnings: u64,
    pub total_minted: u64,
    pub ts: i64,
    pub rng: u64,
    pub deployed_winning_square: u64,
    pub top_miner_username: Option<String>,
    pub top_miner_profile_photo: Option<String>,
}

/// Response type for a user's deploy history event.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct DeployHistoryEvent {
    pub sig: String,
    pub authority: String,
    pub signer: String,
    pub amount: u64,
    pub mask: i64,
    pub round_id: i64,
    pub total_squares: i64,
    pub ts: i64,
    pub winning_square: i64,
    pub top_miner: String,
    pub rewards_sol: u64,
    pub rewards_ore: u64,
    pub total_winnings_sol: u64,
    pub deployed_winning_square: u64,
    pub motherlode: u64,
}

/// Response type for a winner in a round, with user data and betting pattern.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct RoundWinner {
    /// The miner's public key.
    pub authority: String,
    /// User's display name (if set).
    pub username: Option<String>,
    /// User's profile photo URL (if set).
    pub profile_photo_url: Option<String>,
    /// Amount deployed on the winning square (in lamports).
    pub deployed_on_winning: u64,
    /// Combined mask of all squares the user deployed to (bitmask).
    pub combined_mask: u64,
}

/// Response type for listing winners in a round.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct RoundWinnersResponse {
    /// The round ID.
    pub round_id: u64,
    /// The winning square (0-24).
    pub winning_square: u64,
    /// Total amount deployed on the winning square by all miners.
    pub total_on_winning: u64,
    /// Total winnings for the round (in lamports).
    pub total_winnings: u64,
    /// List of winners sorted by deployed_on_winning descending.
    pub winners: Vec<RoundWinner>,
}
