use crate::models::participant::Participant;
use chrono::{DateTime, NaiveDateTime};
use kordophone::model::message::AttachmentMetadata;
use kordophone::model::outgoing_message::OutgoingMessage;
use std::collections::HashMap;
use uuid::Uuid;

#[derive(Clone, Debug)]
pub struct Message {
    pub id: String,
    pub sender: Participant,
    pub text: String,
    pub date: NaiveDateTime,
    pub file_transfer_guids: Vec<String>,
    pub attachment_metadata: Option<HashMap<String, AttachmentMetadata>>,
}

impl Message {
    pub fn builder() -> MessageBuilder {
        MessageBuilder::new()
    }
}

impl From<kordophone::model::Message> for Message {
    fn from(value: kordophone::model::Message) -> Self {
        let sender_participant = match value.sender {
            Some(sender) => Participant::Remote {
                contact_id: None,

                // Weird server quirk: some sender handles are encoded with control characters.
                handle: sender
                    .chars()
                    .filter(|c| {
                        !c.is_control()
                            && !matches!(
                                c,
                                '\u{202A}' | // LRE
                        '\u{202B}' | // RLE  
                        '\u{202C}' | // PDF
                        '\u{202D}' | // LRO
                        '\u{202E}' | // RLO
                        '\u{2066}' | // LRI
                        '\u{2067}' | // RLI
                        '\u{2068}' | // FSI
                        '\u{2069}' // PDI
                            )
                    })
                    .collect::<String>(),
            },

            None => Participant::Me,
        };

        Self {
            id: value.guid,
            sender: sender_participant,
            text: value.text,
            date: DateTime::from_timestamp(
                value.date.unix_timestamp(),
                value.date.unix_timestamp_nanos().try_into().unwrap_or(0),
            )
            .unwrap()
            .naive_local(),
            file_transfer_guids: value.file_transfer_guids,
            attachment_metadata: value.attachment_metadata,
        }
    }
}

impl From<&OutgoingMessage> for Message {
    fn from(value: &OutgoingMessage) -> Self {
        Self {
            id: value.guid.to_string(),
            sender: Participant::Me,
            text: value.text.clone(),
            date: value.date,
            file_transfer_guids: Vec::new(), // Outgoing messages don't have file transfer GUIDs initially
            attachment_metadata: None, // Outgoing messages don't have attachment metadata initially
        }
    }
}

pub struct MessageBuilder {
    id: Option<String>,
    sender: Option<Participant>,
    text: Option<String>,
    date: Option<NaiveDateTime>,
    file_transfer_guids: Option<Vec<String>>,
    attachment_metadata: Option<HashMap<String, AttachmentMetadata>>,
}

impl Default for MessageBuilder {
    fn default() -> Self {
        Self::new()
    }
}

impl MessageBuilder {
    pub fn new() -> Self {
        Self {
            id: None,
            sender: None,
            text: None,
            date: None,
            file_transfer_guids: None,
            attachment_metadata: None,
        }
    }

    pub fn sender(mut self, sender: Participant) -> Self {
        self.sender = Some(sender);
        self
    }

    pub fn text(mut self, text: String) -> Self {
        self.text = Some(text);
        self
    }

    pub fn date(mut self, date: NaiveDateTime) -> Self {
        self.date = Some(date);
        self
    }

    pub fn file_transfer_guids(mut self, file_transfer_guids: Vec<String>) -> Self {
        self.file_transfer_guids = Some(file_transfer_guids);
        self
    }

    pub fn attachment_metadata(
        mut self,
        attachment_metadata: HashMap<String, AttachmentMetadata>,
    ) -> Self {
        self.attachment_metadata = Some(attachment_metadata);
        self
    }

    pub fn build(self) -> Message {
        Message {
            id: self.id.unwrap_or_else(|| Uuid::new_v4().to_string()),
            sender: self.sender.unwrap_or(Participant::Me),
            text: self.text.unwrap_or_default(),
            date: self.date.unwrap_or_else(|| chrono::Utc::now().naive_utc()),
            file_transfer_guids: self.file_transfer_guids.unwrap_or_default(),
            attachment_metadata: self.attachment_metadata,
        }
    }
}
