use std::collections::HashMap;
use std::fmt::{Debug, Display, Formatter};

use crate::{ApAttachment, ApContext, ApDateTime, ApTag};
use anyhow::Result;
use jdt_maybe_multiple::MaybeMultiple;
use serde::{Deserialize, Serialize};

use super::{actor::ApAddress, collection::ApCollectionType, note::ApNoteType, Ephemeral};

#[derive(Serialize, Deserialize, Clone, Debug, Default, Eq, PartialEq)]
pub enum ApQuestionType {
    #[default]
    #[serde(alias = "question")]
    Question,
}

impl Display for ApQuestionType {
    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
        Debug::fmt(self, f)
    }
}

impl TryFrom<String> for ApQuestionType {
    type Error = &'static str;

    fn try_from(kind: String) -> Result<Self, Self::Error> {
        match kind.as_str() {
            "question" => Ok(ApQuestionType::Question),
            _ => Err("no match for {kind}"),
        }
    }
}

impl From<ApQuestionType> for String {
    fn from(kind: ApQuestionType) -> String {
        format!("{kind:#?}")
    }
}

#[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct QuestionCollection {
    total_items: i32,
    // Normally this should not be an Option, but Mitra omits this and causes deserialization failures
    #[serde(rename = "type")]
    kind: Option<ApCollectionType>,
}

impl Debug for QuestionCollection {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.total_items)
    }
}

impl Display for QuestionCollection {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        Debug::fmt(self, f)
    }
}

#[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct QuestionNote {
    id: Option<String>,
    attributed_to: Option<String>,
    to: Option<MaybeMultiple<String>>,
    name: String,
    replies: Option<QuestionCollection>,
    #[serde(rename = "type")]
    kind: ApNoteType,
}

impl Display for QuestionNote {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Option[{}{}]",
            self.name,
            self.replies
                .as_ref()
                .map(|r| format!(", replies: {}", r))
                .unwrap_or_default()
        )
    }
}

impl Debug for QuestionNote {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Option[{}{}{}]",
            self.name,
            self.attributed_to
                .as_ref()
                .map(|a| format!(", by: {}", a))
                .unwrap_or_default(),
            self.replies
                .as_ref()
                .map(|r| format!(", replies: {}", r))
                .unwrap_or_default()
        )
    }
}

#[derive(Serialize, Deserialize, Clone, Default, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ApQuestion {
    #[serde(rename = "@context")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub context: Option<ApContext>,
    #[serde(rename = "type")]
    pub kind: ApQuestionType,
    pub id: String,

    pub attributed_to: ApAddress,
    pub to: MaybeMultiple<ApAddress>,

    #[serde(skip_serializing_if = "MaybeMultiple::is_none")]
    #[serde(default)]
    pub cc: MaybeMultiple<ApAddress>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub end_time: Option<ApDateTime>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub published: Option<ApDateTime>,

    #[serde(skip_serializing_if = "MaybeMultiple::is_none")]
    #[serde(default)]
    pub one_of: MaybeMultiple<QuestionNote>,

    #[serde(skip_serializing_if = "MaybeMultiple::is_none")]
    #[serde(default)]
    pub any_of: MaybeMultiple<QuestionNote>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub content: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub content_map: Option<HashMap<String, String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub summary: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub voters_count: Option<i32>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub url: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub conversation: Option<String>,

    #[serde(skip_serializing_if = "MaybeMultiple::is_none")]
    #[serde(default)]
    pub tag: MaybeMultiple<ApTag>,

    #[serde(skip_serializing_if = "MaybeMultiple::is_none")]
    #[serde(default)]
    pub attachment: MaybeMultiple<ApAttachment>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub sensitive: Option<bool>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub in_reply_to: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub ephemeral: Option<Ephemeral>,
}

impl Display for ApQuestion {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "Poll[{}]", self.id)
    }
}

impl Debug for ApQuestion {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Poll[{}{}{}{}{}{}{}]",
            self.attributed_to,
            self.content
                .as_ref()
                .or(self.summary.as_ref())
                .map(|c| format!(", Q: {:.60}{}", c, if c.len() > 60 { "..." } else { "" }))
                .unwrap_or_default(),
            match (&self.one_of, &self.any_of) {
                (MaybeMultiple::None, MaybeMultiple::None) => String::new(),
                (other, MaybeMultiple::None) => format!(", one_of: {}", other),
                (MaybeMultiple::None, other) => format!(", any_of: {}", other),
                _ => String::new(), // Should never happen as a poll can't have both
            },
            self.voters_count
                .map(|c| format!(", votes: {}", c))
                .unwrap_or_default(),
            self.end_time
                .as_ref()
                .map(|t| format!(", ends: {}", t))
                .unwrap_or_default(),
            self.in_reply_to
                .as_ref()
                .map(|r| format!(", reply_to: {}", r))
                .unwrap_or_default(),
            if self.sensitive.unwrap_or(false) {
                ", sensitive"
            } else {
                ""
            }
        )
    }
}

impl ApQuestion {
    pub fn dedup(mut self) -> Self {
        if let Some(mut ephemeral) = self.ephemeral {
            if let Some(mut announces) = ephemeral.announces {
                announces.sort();
                announces.dedup();
                ephemeral.announces = Some(announces);
            }

            if let Some(mut likes) = ephemeral.likes {
                likes.sort();
                likes.dedup();
                ephemeral.likes = Some(likes);
            }

            self.ephemeral = Some(ephemeral);
        }

        self
    }
}
