package org.jivesoftware.smackx.pubsub;

import org.junit.jupiter.api.Test;
import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;


public class PubSubUriTest {

    /**
     * Parse a URI that points to a pubsub node.
     *
     * @see <a href="https://xmpp.org/extensions/xep-0060.html#example-227">
     *     XEP-0060: Publish-Subscribe: Example 227. XMPP URI for a node</a>
     * @throws XmppStringprepException not expected
     */
    @Test
    public void pubSubNodeUriTest() throws XmppStringprepException {
        final String uriString = "xmpp:pubsub.shakespeare.lit?;node=princely_musings";
        PubSubUri uri = PubSubUri.from(uriString);

        assertEquals(JidCreate.domainBareFromOrThrowUnchecked("pubsub.shakespeare.lit"), uri.getService());
        assertEquals("princely_musings", uri.getNode());
        assertNull(uri.getItem());
        assertNull(uri.getAction());
        assertEquals(uriString, uri.toString());
    }

    /**
     * Parse a URI that points to an item inside a pubsub node.
     *
     * @see <a href="https://xmpp.org/extensions/xep-0060.html#example-228">
     *     XEP-0060: Publish-Subscribe: Example 228. XMPP URI for a pubsub item</a>
     * @throws XmppStringprepException not expected
     */
    @Test
    public void pubSubItemInNodeUriTest() throws XmppStringprepException {
        final String uriString = "xmpp:pubsub.shakespeare.lit?;node=princely_musings;item=ae890ac52d0df67ed7cfdf51b644e901";
        PubSubUri uri = PubSubUri.from(uriString);

        assertEquals(JidCreate.domainBareFromOrThrowUnchecked("pubsub.shakespeare.lit"), uri.getService());
        assertEquals("princely_musings", uri.getNode());
        assertEquals("ae890ac52d0df67ed7cfdf51b644e901", uri.getItem());
        assertNull(uri.getAction());
        assertEquals(uriString, uri.toString());
    }

    /**
     * Parse a URI for subscribing to a pubsub node.
     *
     * @see <a href="https://xmpp.org/extensions/xep-0060.html#example-229">
     *     XEP-0060: Publish-Subscribe: Example 229. URI for subscribing to a pubsub node</a>
     * @throws XmppStringprepException not expected
     */
    @Test
    public void pubSubSubscribeToNodeUriTest() throws XmppStringprepException {
        final String uriString = "xmpp:pubsub.shakespeare.lit?pubsub;action=subscribe;node=princely_musings";
        PubSubUri uri = PubSubUri.from(uriString);

        assertEquals(JidCreate.domainBareFromOrThrowUnchecked("pubsub.shakespeare.lit"), uri.getService());
        assertEquals("princely_musings", uri.getNode());
        assertNull(uri.getItem());
        assertEquals(PubSubUri.Action.subscribe, uri.getAction());
        assertEquals(uriString, uri.toString());
    }

    /**
     * Parse a URI for retrieving a single item from a specific node.
     *
     * @see <a href="https://xmpp.org/extensions/xep-0060.html#example-230">
     *     XEP-0060: Publish-Subscribe: Example 230. URI for retrieving a pubsub item</a>
     * @throws XmppStringprepException not expected
     */
    @Test
    public void pubSubRetrieveItemFromNodeUriTest() throws XmppStringprepException {
        final String uriString = "xmpp:pubsub.shakespeare.lit?pubsub;action=retrieve;node=princely_musings;item=ae890ac52d0df67ed7cfdf51b644e901";
        PubSubUri uri = PubSubUri.from(uriString);

        assertEquals(JidCreate.domainBareFromOrThrowUnchecked("pubsub.shakespeare.lit"), uri.getService());
        assertEquals(PubSubUri.Action.retrieve, uri.getAction());
        assertEquals("princely_musings", uri.getNode());
        assertEquals("ae890ac52d0df67ed7cfdf51b644e901", uri.getItem());
        assertEquals(uriString, uri.toString());
    }

    /**
     * Parse a URI for unsubscribing from a specific pubsub node.
     *
     * @see <a href="https://xmpp.org/extensions/xep-0060.html#example-233">
     *     XEP-0060: Publish-Subscribe: Example 233. Pubsub Unsubscribe Action: IRI/URI</a>
     * @throws XmppStringprepException not expected
     */
    @Test
    public void pubSubUnsubscribeFromNodeUriTest() throws XmppStringprepException {
        final String uriString = "xmpp:pubsub.shakespeare.lit?pubsub;action=unsubscribe;node=princely_musings";
        PubSubUri uri = PubSubUri.from(uriString);

        assertEquals(JidCreate.domainBareFromOrThrowUnchecked("pubsub.shakespeare.lit"), uri.getService());
        assertEquals(PubSubUri.Action.unsubscribe, uri.getAction());
        assertEquals("princely_musings", uri.getNode());
        assertNull(uri.getItem());
        assertEquals(uriString, uri.toString());
    }

    /**
     * Parse a URI for retrieving multiple items from a particular pubsub node.
     * @see <a href="https://xmpp.org/extensions/xep-0060.html#example-235">
     *     XEP-0060: Publish-Subscribe: Example 235. Pubsub Retrieve Action: IRI/URI</a>
     */
    @Test
    public void pubSubRetrieveItemsFromNodeUriTest() throws XmppStringprepException {
        final String uriString = "xmpp:pubsub.shakespeare.lit?pubsub;action=retrieve;node=princely_musings";
        PubSubUri uri = PubSubUri.from(uriString);

        assertEquals(JidCreate.domainBareFromOrThrowUnchecked("pubsub.shakespeare.lit"), uri.getService());
        assertEquals(PubSubUri.Action.retrieve, uri.getAction());
        assertEquals("princely_musings", uri.getNode());
        assertNull(uri.getItem());
        assertEquals(uriString, uri.toString());
    }

    @Test
    public void serviceJidsAreParsedToCorrectJidTypes() throws XmppStringprepException {
        final String uriWithDomainJid = "xmpp:pubsub.shakespeare.lit?;node=princely_musings";
        final String uriWithEntityJid = "xmpp:hamlet@denmark.lit?;node=blog";

        PubSubUri withDomainJid = PubSubUri.from(uriWithDomainJid);
        PubSubUri withEntityJid = PubSubUri.from(uriWithEntityJid);

        assertTrue(withDomainJid.getService() instanceof DomainBareJid);
        assertTrue(withEntityJid.getService() instanceof EntityBareJid);
    }

    /**
     * Ensure that parsing a uri with an invalid scheme ('xmpps' in this case) will cause a
     * {@link XmppStringprepException} to be thrown.
     */
    @Test
    public void invalidSchemeCausesThrowingTest() {
        assertThrows(XmppStringprepException.class,
                () -> PubSubUri.from("xmpps:pubsub.shakespeare.lit?;node=princely_musings"));
    }

    @Test
    public void fullJidCausesThrowingTest() {
        assertThrows(XmppStringprepException.class,
                () -> PubSubUri.from("xmpp:alice@wonderland.lit/illegalResourcePart?:node=rabbit_hole"));
    }
}
