use minidom::Element;
use xmpp_parsers::disco::{DiscoInfoQuery, DiscoInfoResult, DiscoItemsResult, Feature, Identity};
use xmpp_parsers::iq::Iq;
use xmpp_parsers::message::{Body, Message, MessageType};
use xmpp_parsers::ping::Ping;
use xmpp_parsers::presence::{Presence, Show as PresenceShow, Type as PresenceType};
use xmpp_parsers::roster::{Item, Roster};
use xmpp_parsers::stanza_error::{DefinedCondition, ErrorType, StanzaError};
use xmpp_parsers::version::VersionResult;
use xmpp_parsers::{BareJid, Jid};

pub fn make_presence(id: String, show: Option<PresenceShow>, text: String) -> Element {
    let mut presence = Presence::new(PresenceType::None);
    presence.id = Some(id);
    presence.show = show;
    presence.statuses.insert(String::from("en"), text);
    presence.into()
}

pub fn make_get_roster(id: &str) -> Element {
    Iq::from_get(
        id,
        Roster {
            items: vec![],
            ver: None,
        },
    )
    .into()
}

pub fn make_add_roster(id: &str, jid: xmpp_parsers::BareJid) -> Element {
    Iq::from_set(
        id,
        Roster {
            items: vec![Item {
                jid,
                name: None,
                subscription: xmpp_parsers::roster::Subscription::None,
                ask: xmpp_parsers::roster::Ask::None,
                groups: vec![],
            }],
            ver: None,
        },
    )
    .into()
}

pub fn make_roster_push_answer(
    id: String,
    from: xmpp_parsers::Jid,
    to: Option<xmpp_parsers::Jid>,
) -> Iq {
    let mut answer = Iq::from_result(id, None as Option<Roster>);
    answer.from = Some(from);
    answer.to = to;
    answer
}

pub fn make_ask_subscribe(id: String, jid: xmpp_parsers::Jid) -> Element {
    let mut presence = Presence::new(PresenceType::Subscribe);
    presence.id = Some(id);
    presence.to = Some(jid);
    presence.into()
}

pub fn make_allow_subscribe(id: String, jid: xmpp_parsers::Jid) -> Element {
    let mut presence = Presence::new(PresenceType::Subscribed);
    presence.id = Some(id);
    presence.to = Some(jid);
    presence.into()
}

pub fn make_chat_message(id: String, jid: xmpp_parsers::Jid, text: String) -> Element {
    let mut message = Message::new(Some(jid));
    message.id = Some(id);
    message.bodies.insert(String::new(), Body(text));
    message.type_ = MessageType::Chat;
    message
        .payloads
        .push(xmpp_parsers::receipts::Request {}.into());
    message.into()
}

pub fn make_muc_presence(
    id: &str,
    from: xmpp_parsers::Jid,
    to: xmpp_parsers::Jid,
    show: Option<PresenceShow>,
    text: Option<String>,
) -> Element {
    let mut presence = Presence::new(PresenceType::None);
    presence.from = Some(from);
    presence.to = Some(to);
    presence.id = Some(id.to_string());
    presence.show = show;
    if let Some(text) = text {
        presence.statuses.insert(String::from("en"), text);
    }
    presence.add_payload(xmpp_parsers::muc::Muc::new());
    presence.into()
}

pub fn make_muc_message(to: xmpp_parsers::Jid, text: String) -> Element {
    let mut message = Message::new(Some(Jid::Bare(to.into())));
    message.bodies.insert(String::new(), Body(text));
    message.type_ = MessageType::Groupchat;
    message.into()
}

pub fn make_muc_presence_leave(from: xmpp_parsers::Jid, to: xmpp_parsers::Jid) -> Element {
    let mut presence = Presence::new(PresenceType::Unavailable);
    presence.from = Some(from);
    presence.to = Some(to);
    presence.into()
}

pub fn make_ping(id: &str, from: xmpp_parsers::Jid, to: Option<xmpp_parsers::Jid>) -> Element {
    let ping = Iq::from_get(id, Ping)
        .with_to(to.unwrap_or_else(|| Jid::Bare(BareJid::domain(from.clone().domain()))))
        .with_from(from);
    ping.into()
}

pub fn make_pong(id: String, from: xmpp_parsers::Jid, to: Option<xmpp_parsers::Jid>) -> Iq {
    let mut pong = Iq::from_result(id, None as Option<Roster>);
    pong.from = Some(from);
    pong.to = to;
    pong
}

pub fn make_version(id: String, from: xmpp_parsers::Jid, to: Option<xmpp_parsers::Jid>) -> Iq {
    let mut version = Iq::from_result(
        id,
        Some(VersionResult {
            name: "SendXmppDRust".to_string(),
            version: "0.1.0".to_string(),
            os: None,
        }),
    );
    version.from = Some(from);
    version.to = to;
    version
}

pub fn make_iq_unsupported_error(
    id: String,
    from: xmpp_parsers::Jid,
    to: Option<xmpp_parsers::Jid>,
) -> Iq {
    let mut error = Iq::from_error(
        id,
        StanzaError {
            type_: ErrorType::Cancel,
            by: Some(from.clone()),
            defined_condition: DefinedCondition::ServiceUnavailable,
            texts: std::collections::BTreeMap::new(),
            other: None,
        },
    );
    error.from = Some(from);
    error.to = to;
    error
}

pub fn make_disco_info_result(
    id: String,
    from: xmpp_parsers::Jid,
    to: Option<xmpp_parsers::Jid>,
) -> Iq {
    let mut result = Iq::from_result(
        id,
        Some(DiscoInfoResult {
            node: None,
            identities: vec![Identity {
                category: "client".to_string(),
                type_: "bot".to_string(),
                name: None,
                lang: None,
            }],
            features: vec![
                Feature {
                    var: "http://jabber.org/protocol/disco#info".to_string(),
                },
                Feature {
                    var: "urn:xmpp:ping".to_string(),
                },
                Feature {
                    var: "http://jabber.org/protocol/commands".to_string(),
                },
            ],
            extensions: vec![],
        }),
    );
    result.from = Some(from);
    result.to = to;
    result
}

pub fn make_disco_items_result(
    id: String,
    from: xmpp_parsers::Jid,
    to: Option<xmpp_parsers::Jid>,
) -> Iq {
    let mut result = Iq::from_result(
        id,
        Some(DiscoItemsResult {
            node: None,
            items: vec![],
        }),
    );
    result.from = Some(from);
    result.to = to;
    result
}

pub fn make_disco_items_commands(
    id: String,
    from: xmpp_parsers::Jid,
    to: Option<xmpp_parsers::Jid>,
) -> Iq {
    let mut result = Iq::from_result(
        id,
        Some(DiscoItemsResult {
            node: Some("http://jabber.org/protocol/commands".to_string()),
            items: vec![],
        }),
    );
    result.from = Some(from);
    result.to = to;
    result
}

pub fn make_disco_get(
    id: String,
    from: Option<xmpp_parsers::Jid>,
    to: Option<xmpp_parsers::Jid>,
) -> Element {
    let mut get = Iq::from_get(id, DiscoInfoQuery { node: None });
    get.from = from;
    get.to = to;
    get.into()
}