use crate::enums::event::Event;
use crate::enums::internal_event::InternalEvent;
use crate::enums::msnp_list::MsnpList;
use crate::enums::msnp_status::MsnpStatus;
use crate::models::personal_message::PersonalMessage;
use crate::models::presence::Presence;
use core::str;
use std::borrow::Cow;

pub fn into_event(message: &Vec<u8>) -> Option<Event> {
    let reply = unsafe { str::from_utf8_unchecked(message.as_slice()) };
    let command = reply.lines().next().unwrap_or_default().to_string() + "\r\n";

    let args: Vec<&str> = command.split_ascii_whitespace().collect();
    match *args.first().unwrap_or(&"") {
        "GTC" => {
            if args.len() < 3
                && let Some(gtc) = args.get(1)
            {
                Some(Event::Gtc(gtc.to_string()))
            } else {
                None
            }
        }

        "BLP" => {
            if args.len() < 3
                && let Some(blp) = args.get(1)
            {
                Some(Event::Blp(blp.to_string()))
            } else {
                None
            }
        }

        "PRP" => {
            if args.len() < 4
                && *args.get(1).unwrap_or(&"") == "MFN"
                && let Some(display_name) = args.get(2)
            {
                Some(Event::DisplayName(
                    urlencoding::decode(display_name)
                        .unwrap_or(Cow::from(*display_name))
                        .to_string(),
                ))
            } else {
                None
            }
        }

        "LSG" => {
            if let Some(email) = args.get(1)
                && let Some(guid) = args.get(2)
            {
                Some(Event::Group {
                    name: urlencoding::decode(email)
                        .unwrap_or(Cow::from(*email))
                        .to_string(),
                    guid: guid.to_string(),
                })
            } else {
                None
            }
        }

        "LST" => {
            let email = args.get(1)?;
            let mut lists: Vec<MsnpList> = Vec::new();
            let lists_number_index = if args.len() > 4 { 4 } else { 3 };

            let lists_number = args
                .get(lists_number_index)
                .unwrap_or(&"")
                .parse::<u32>()
                .ok()?;

            if lists_number & 1 == 1 {
                lists.push(MsnpList::ForwardList);
            }

            if lists_number & 2 == 2 {
                lists.push(MsnpList::AllowList);
            }

            if lists_number & 4 == 4 {
                lists.push(MsnpList::BlockList);
            }

            if lists_number & 8 == 8 {
                lists.push(MsnpList::ReverseList);
            }

            if lists_number & 16 == 16 {
                lists.push(MsnpList::PendingList);
            }

            if lists_number & 1 == 1
                && let Some(guid) = args.get(3)
            {
                let mut groups: Vec<String> = Vec::new();
                if args.len() > 5 {
                    groups = args[5].split(",").map(|id| id.to_string()).collect();
                }

                Some(Event::ContactInForwardList {
                    email: email.replace("N=", ""),
                    display_name: urlencoding::decode(
                        &args.get(2).unwrap_or(email).replace("F=", ""),
                    )
                    .unwrap_or(Cow::from(*email))
                    .to_string(),
                    guid: guid.replace("C=", ""),
                    groups,
                    lists,
                })
            } else {
                Some(Event::Contact {
                    email: email.replace("N=", ""),
                    display_name: urlencoding::decode(
                        args.get(2).unwrap_or(email).replace("F=", "").as_str(),
                    )
                    .unwrap_or(Cow::from(*email))
                    .to_string(),
                    lists,
                })
            }
        }

        "ILN" => {
            let email = args.get(3)?;
            let msn_object = if args.len() > 6 {
                urlencoding::decode(args[6]).ok().map(String::from)
            } else {
                None
            };

            let status = match *args.get(2)? {
                "BSY" => MsnpStatus::Busy,
                "AWY" => MsnpStatus::Away,
                "IDL" => MsnpStatus::Idle,
                "LUN" => MsnpStatus::OutToLunch,
                "PHN" => MsnpStatus::OnThePhone,
                "BRB" => MsnpStatus::BeRightBack,
                _ => MsnpStatus::Online,
            };

            Some(Event::InitialPresenceUpdate {
                email: email.to_string(),
                display_name: urlencoding::decode(args.get(4).unwrap_or(email))
                    .unwrap_or(Cow::from(*email))
                    .to_string(),
                presence: Presence {
                    status,
                    client_id: args.get(5).unwrap_or(&"").parse().unwrap_or(0),
                    msn_object: if let Some(msn_object) = &msn_object {
                        quick_xml::de::from_str(msn_object).ok()
                    } else {
                        None
                    },
                    msn_object_string: msn_object,
                },
            })
        }

        "NLN" => {
            let email = args.get(2)?;
            let msn_object = if args.len() > 5 {
                urlencoding::decode(args[5]).ok().map(String::from)
            } else {
                None
            };

            let status = match *args.get(1)? {
                "BSY" => MsnpStatus::Busy,
                "AWY" => MsnpStatus::Away,
                "IDL" => MsnpStatus::Idle,
                "LUN" => MsnpStatus::OutToLunch,
                "PHN" => MsnpStatus::OnThePhone,
                "BRB" => MsnpStatus::BeRightBack,
                _ => MsnpStatus::Online,
            };

            Some(Event::PresenceUpdate {
                email: email.to_string(),
                display_name: urlencoding::decode(args.get(3).unwrap_or(email))
                    .unwrap_or(Cow::from(*email))
                    .to_string(),
                presence: Presence {
                    status,
                    client_id: args.get(4).unwrap_or(&"").parse().unwrap_or(0),
                    msn_object: if let Some(msn_object) = &msn_object {
                        quick_xml::de::from_str(msn_object).ok()
                    } else {
                        None
                    },
                    msn_object_string: msn_object,
                },
            })
        }

        "UBX" => {
            let email = args.get(1)?;
            let payload = reply.replace(command.as_str(), "");

            let personal_message =
                quick_xml::de::from_str(payload.as_str()).unwrap_or(PersonalMessage {
                    psm: "".to_string(),
                    current_media: "".to_string(),
                });

            Some(Event::PersonalMessageUpdate {
                email: email.to_string(),
                personal_message,
            })
        }

        "FLN" => args.get(1).map(|email| Event::ContactOffline {
            email: email.to_string(),
        }),

        "ADC" => {
            if *args.get(1).unwrap_or(&"") == "0"
                && *args.get(2).unwrap_or(&"") == "RL"
                && let Some(email) = args.get(3)
                && let Some(display_name) = args.get(4)
            {
                Some(Event::AddedBy {
                    email: email.replace("N=", ""),
                    display_name: urlencoding::decode(display_name.replace("F=", "").as_str())
                        .unwrap_or(Cow::from(*email))
                        .to_string(),
                })
            } else {
                None
            }
        }

        "REM" => {
            if *args.get(1).unwrap_or(&"") == "0"
                && *args.get(2).unwrap_or(&"") == "RL"
                && let Some(email) = args.get(3)
            {
                Some(Event::RemovedBy(email.replace("N=", "")))
            } else {
                None
            }
        }

        "MSG" => {
            let payload = reply.replace(&command, "");
            let mut payload_lines = payload.lines();
            let content_type = payload_lines.nth(1)?;

            if content_type.contains("application/x-msmsgssystemmessage") {
                let message_type = payload_lines.nth(1)?;
                let (_, message_type) = message_type.split_once(":")?;
                let message_type = message_type.trim();

                if message_type == "1" {
                    let time_remaining = payload_lines.next()?;
                    let (_, time_remaining) = time_remaining.split_once(":")?;

                    let time_remaining = time_remaining.trim().parse::<u32>().ok()?;
                    return Some(Event::ServerMaintenanceScheduled { time_remaining });
                }
            }

            None
        }

        "OUT" => {
            if args.len() > 1 && *args.get(1).unwrap_or(&"") == "OTH" {
                return Some(Event::LoggedInAnotherDevice);
            }

            Some(Event::Disconnected)
        }

        _ => None,
    }
}

pub fn into_internal_event(message: &Vec<u8>) -> InternalEvent {
    let reply = unsafe { str::from_utf8_unchecked(message.as_slice()) }.to_string();
    let command = reply.lines().next().unwrap_or_default().to_string() + "\r\n";

    let args: Vec<&str> = command.split_ascii_whitespace().collect();
    match *args.first().unwrap_or(&"") {
        "RNG" => {
            let mut server_and_port = args.get(2).unwrap_or(&"").split(":");
            if let Some(server) = server_and_port.next()
                && let Some(port) = server_and_port.next()
                && let Some(session_id) = args.get(1)
                && let Some(cki_string) = args.get(4)
            {
                InternalEvent::SwitchboardInvitation {
                    server: server.to_string(),
                    port: port.to_string(),
                    session_id: session_id.to_string(),
                    cki_string: cki_string.to_string(),
                }
            } else {
                InternalEvent::ServerReply(reply)
            }
        }

        _ => InternalEvent::ServerReply(reply),
    }
}
