use chrono::DateTime;
use chrono::NaiveDateTime;

use crate::daemon::attachment_store::AttachmentStore;
use crate::daemon::models::Attachment;
use kordophone::model::message::AttachmentMetadata;
use kordophone::model::outgoing_message::OutgoingMessage;
use kordophone_db::models::participant::Participant as DbParticipant;
use std::collections::HashMap;

#[derive(Clone, Debug)]
pub enum Participant {
    Me,
    Remote {
        handle: String,
        contact_id: Option<String>,
    },
}

impl From<String> for Participant {
    fn from(display_name: String) -> Self {
        Participant::Remote {
            handle: display_name,
            contact_id: None,
        }
    }
}

impl From<&str> for Participant {
    fn from(display_name: &str) -> Self {
        Participant::Remote {
            handle: display_name.to_string(),
            contact_id: None,
        }
    }
}

impl From<kordophone_db::models::Participant> for Participant {
    fn from(participant: kordophone_db::models::Participant) -> Self {
        match participant {
            kordophone_db::models::Participant::Me => Participant::Me,
            kordophone_db::models::Participant::Remote { handle, contact_id } => {
                Participant::Remote { handle, contact_id }
            }
        }
    }
}

impl Participant {
    pub fn display_name(&self) -> String {
        match self {
            Participant::Me => "(Me)".to_string(),
            Participant::Remote { handle, .. } => handle.clone(),
        }
    }
}

#[derive(Clone, Debug)]
pub struct Message {
    pub id: String,
    pub sender: Participant,
    pub text: String,
    pub date: NaiveDateTime,
    pub attachments: Vec<Attachment>,
}

fn attachments_from(
    file_transfer_guids: &Vec<String>,
    attachment_metadata: &Option<HashMap<String, AttachmentMetadata>>,
) -> Vec<Attachment> {
    file_transfer_guids
        .iter()
        .map(|guid| {
            let mut attachment = AttachmentStore::get_attachment_impl(
                &AttachmentStore::get_default_store_path(),
                guid,
            );
            attachment.metadata = match attachment_metadata {
                Some(attachment_metadata) => attachment_metadata
                    .get(guid)
                    .cloned()
                    .map(|metadata| metadata.into()),
                None => None,
            };

            attachment
        })
        .collect()
}

impl From<kordophone_db::models::Message> for Message {
    fn from(message: kordophone_db::models::Message) -> Self {
        let attachments =
            attachments_from(&message.file_transfer_guids, &message.attachment_metadata);
        Self {
            id: message.id,
            sender: message.sender.into(),
            text: message.text,
            date: message.date,
            attachments,
        }
    }
}

impl From<Message> for kordophone_db::models::Message {
    fn from(message: Message) -> Self {
        Self {
            id: message.id,
            sender: match message.sender {
                Participant::Me => kordophone_db::models::Participant::Me,
                Participant::Remote { handle, contact_id } => {
                    kordophone_db::models::Participant::Remote { handle, contact_id }
                }
            },
            text: message.text,
            date: message.date,
            file_transfer_guids: message.attachments.iter().map(|a| a.guid.clone()).collect(),
            attachment_metadata: {
                let metadata_map: HashMap<String, kordophone::model::message::AttachmentMetadata> =
                    message
                        .attachments
                        .iter()
                        .filter_map(|a| {
                            a.metadata
                                .as_ref()
                                .map(|m| (a.guid.clone(), m.clone().into()))
                        })
                        .collect();
                if metadata_map.is_empty() {
                    None
                } else {
                    Some(metadata_map)
                }
            },
        }
    }
}

impl From<kordophone::model::Message> for Message {
    fn from(message: kordophone::model::Message) -> Self {
        let attachments =
            attachments_from(&message.file_transfer_guids, &message.attachment_metadata);
        Self {
            id: message.guid,
            sender: match message.sender {
                Some(sender) => Participant::Remote {
                    handle: sender,
                    contact_id: None,
                },
                None => Participant::Me,
            },
            text: message.text,
            date: DateTime::from_timestamp(
                message.date.unix_timestamp(),
                message.date.unix_timestamp_nanos().try_into().unwrap_or(0),
            )
            .unwrap()
            .naive_local(),
            attachments,
        }
    }
}

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,
            attachments: Vec::new(), // Outgoing messages don't have attachments initially
        }
    }
}

impl From<Participant> for DbParticipant {
    fn from(participant: Participant) -> Self {
        match participant {
            Participant::Me => DbParticipant::Me,
            Participant::Remote { handle, contact_id } => DbParticipant::Remote {
                handle,
                contact_id: contact_id.clone(),
            },
        }
    }
}
