package com.fsck.k9.provider;


import java.util.Arrays;
import java.util.Collections;
import java.util.GregorianCalendar;

import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.ProviderTestCase2;

import com.fsck.k9.Account;
import com.fsck.k9.Preferences;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.internet.MimeMessage;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;


@RunWith(AndroidJUnit4.class)
public class EmailProviderTest extends ProviderTestCase2<EmailProvider> {
    private MimeMessage message;
    private MimeMessage laterMessage;
    private MimeMessage reply;
    private MimeMessage replyAtSameTime;


    public EmailProviderTest() {
        super(EmailProvider.class, EmailProvider.AUTHORITY);
    }

    private void buildMessages() throws MessagingException {
        message = new MimeMessage();
        message.setSubject("Test Subject");
        message.setSentDate(new GregorianCalendar(2016, 1, 2).getTime(), false);
        message.setMessageId("<uid001@email.com>");

        laterMessage = new MimeMessage();
        laterMessage.setSubject("Test Subject2");
        laterMessage.setSentDate(new GregorianCalendar(2016, 1, 3).getTime(), false);

        reply = new MimeMessage();
        reply.setSubject("Re: Test Subject");
        reply.setSentDate(new GregorianCalendar(2016, 1, 3).getTime(), false);
        reply.setMessageId("<uid002@email.com>");
        reply.setInReplyTo("<uid001@email.com>");

        replyAtSameTime = new MimeMessage();
        replyAtSameTime.setSubject("Re: Test Subject");
        replyAtSameTime.setSentDate(new GregorianCalendar(2016, 1, 2).getTime(), false);
        replyAtSameTime.setMessageId("<uid002@email.com>");
        replyAtSameTime.setInReplyTo("<uid001@email.com>");
    }

    @Before
    @Override
    public void setUp() throws Exception {
        setContext(InstrumentationRegistry.getTargetContext());
        super.setUp();
        buildMessages();
    }

    @Test
    public void onCreate_shouldReturnTrue() {
        assertNotNull(getProvider());

        boolean returnValue = getProvider().onCreate();

        assertEquals(true, returnValue);
    }

    @Test(expected = IllegalArgumentException.class)
    public void query_withInvalidURI_throwsIllegalArgumentException() {
        getProvider().query(
                Uri.parse("content://com.google.www"),
                new String[] {},
                "",
                new String[] {},
                "");
    }

    @Test(expected = IllegalArgumentException.class)
    public void query_forMessagesWithInvalidAccount_throwsIllegalArgumentException() {
        Cursor cursor = getProvider().query(
                Uri.parse("content://" + EmailProvider.AUTHORITY + "/account/1/messages"),
                new String[] {},
                "",
                new String[] {},
                "");

        assertNotNull(cursor);
    }

    @Test(expected = IllegalArgumentException.class)
    public void query_forMessagesWithAccountAndWithoutRequiredFields_throwsIllegalArgumentException() {
        Account account = Preferences.getPreferences(getContext()).newAccount();
        account.getUuid();

        Cursor cursor = getProvider().query(
                Uri.parse("content://" + EmailProvider.AUTHORITY + "/account/" + account.getUuid() + "/messages"),
                new String[] {},
                "",
                new String[] {},
                "");

        assertNotNull(cursor);
        assertTrue(cursor.isAfterLast());
    }

    @Test(expected = SQLException.class) //Handle this better?
    public void query_forMessagesWithAccountAndRequiredFieldsWithNoOrderBy_throwsSQLiteException() {
        Account account = Preferences.getPreferences(getContext()).newAccount();
        account.getUuid();

        Cursor cursor = getProvider().query(
                Uri.parse("content://" + EmailProvider.AUTHORITY + "/account/" + account.getUuid() + "/messages"),
                new String[] {
                        EmailProvider.MessageColumns.ID,
                        EmailProvider.MessageColumns.FOLDER_ID,
                        EmailProvider.ThreadColumns.ROOT
                },
                "",
                new String[] {},
                "");

        assertNotNull(cursor);
        assertTrue(cursor.isAfterLast());
    }

    @Test
    public void query_forMessagesWithEmptyAccountAndRequiredFieldsAndOrderBy_providesEmptyResult() {
        Account account = Preferences.getPreferences(getContext()).newAccount();
        account.getUuid();

        Cursor cursor = getProvider().query(
                Uri.parse("content://" + EmailProvider.AUTHORITY + "/account/" + account.getUuid() + "/messages"),
                new String[] {
                        EmailProvider.MessageColumns.ID,
                        EmailProvider.MessageColumns.FOLDER_ID,
                        EmailProvider.ThreadColumns.ROOT,
                        EmailProvider.MessageColumns.SUBJECT
                },
                "",
                new String[] {},
                EmailProvider.MessageColumns.DATE);

        assertNotNull(cursor);
        assertFalse(cursor.moveToFirst());
    }

    @Test
    public void query_forMessagesWithAccountAndRequiredFieldsAndOrderBy_providesResult() throws MessagingException {
        Account account = Preferences.getPreferences(getContext()).newAccount();
        account.getUuid();
        account.getLocalStore().getFolder("Inbox").appendMessages(Collections.singletonList(message));

        Cursor cursor = getProvider().query(
                Uri.parse("content://" + EmailProvider.AUTHORITY + "/account/" + account.getUuid() + "/messages"),
                new String[] {
                        EmailProvider.MessageColumns.ID,
                        EmailProvider.MessageColumns.FOLDER_ID,
                        EmailProvider.ThreadColumns.ROOT,
                        EmailProvider.MessageColumns.SUBJECT },
                "",
                new String[] {},
                EmailProvider.MessageColumns.DATE);

        assertNotNull(cursor);
        assertTrue(cursor.moveToFirst());
        assertEquals(message.getSubject(), cursor.getString(3));
    }

    @Test
    public void query_forMessagesWithAccountAndRequiredFieldsAndOrderBy_sortsCorrectly() throws MessagingException {
        Account account = Preferences.getPreferences(getContext()).newAccount();
        account.getUuid();
        account.getLocalStore().getFolder("Inbox").appendMessages(Arrays.asList(message, laterMessage));

        Cursor cursor = getProvider().query(
                Uri.parse("content://" + EmailProvider.AUTHORITY + "/account/" + account.getUuid() + "/messages"),
                new String[] {
                        EmailProvider.MessageColumns.ID,
                        EmailProvider.MessageColumns.FOLDER_ID,
                        EmailProvider.ThreadColumns.ROOT,
                        EmailProvider.MessageColumns.SUBJECT
                },
                "",
                new String[] {},
                EmailProvider.MessageColumns.DATE + " DESC");

        assertNotNull(cursor);
        assertTrue(cursor.moveToFirst());
        assertEquals(laterMessage.getSubject(), cursor.getString(3));
        cursor.moveToNext();
        assertEquals(message.getSubject(), cursor.getString(3));
    }

    @Test
    public void query_forThreadedMessages_sortsCorrectly() throws MessagingException {
        Account account = Preferences.getPreferences(getContext()).newAccount();
        account.getUuid();
        account.getLocalStore().getFolder("Inbox").appendMessages(Arrays.asList(message, laterMessage));

        Cursor cursor = getProvider().query(
                Uri.parse("content://" + EmailProvider.AUTHORITY + "/account/" + account.getUuid() +
                        "/messages/threaded"),
                new String[] {
                        EmailProvider.MessageColumns.ID,
                        EmailProvider.MessageColumns.FOLDER_ID,
                        EmailProvider.ThreadColumns.ROOT,
                        EmailProvider.MessageColumns.SUBJECT,
                        EmailProvider.MessageColumns.DATE
                },
                "",
                new String[] {},
                EmailProvider.MessageColumns.DATE + " DESC");
        assertNotNull(cursor);
        assertTrue(cursor.moveToFirst());
        assertEquals(laterMessage.getSubject(), cursor.getString(3));
        cursor.moveToNext();
        assertEquals(message.getSubject(), cursor.getString(3));
    }

    @Test
    public void query_forThreadedMessages_showsThreadOfEmailOnce() throws MessagingException {
        Account account = Preferences.getPreferences(getContext()).newAccount();
        account.getUuid();
        account.getLocalStore().getFolder("Inbox").appendMessages(Collections.singletonList(message));
        account.getLocalStore().getFolder("Inbox").appendMessages(Collections.singletonList(reply));

        Cursor cursor = getProvider().query(
                Uri.parse("content://" + EmailProvider.AUTHORITY + "/account/" + account.getUuid() +
                        "/messages/threaded"),
                new String[] {
                        EmailProvider.MessageColumns.ID,
                        EmailProvider.MessageColumns.FOLDER_ID,
                        EmailProvider.ThreadColumns.ROOT,
                        EmailProvider.MessageColumns.SUBJECT,
                        EmailProvider.MessageColumns.DATE,
                        EmailProvider.SpecialColumns.THREAD_COUNT
                },
                "",
                new String[] {},
                EmailProvider.MessageColumns.DATE + " DESC");

        assertNotNull(cursor);
        assertTrue(cursor.moveToFirst());
        assertEquals(2, cursor.getInt(5));
        assertFalse(cursor.moveToNext());
    }

    @Test
    public void query_forThreadedMessages_showsThreadOfEmailWithSameSendTimeOnce() throws MessagingException {
        Account account = Preferences.getPreferences(getContext()).newAccount();
        account.getUuid();
        account.getLocalStore().getFolder("Inbox").appendMessages(Collections.singletonList(message));
        account.getLocalStore().getFolder("Inbox").appendMessages(Collections.singletonList(replyAtSameTime));

        Cursor cursor = getProvider().query(
                Uri.parse("content://" + EmailProvider.AUTHORITY + "/account/" + account.getUuid() +
                        "/messages/threaded"),
                new String[] {
                        EmailProvider.MessageColumns.ID,
                        EmailProvider.MessageColumns.FOLDER_ID,
                        EmailProvider.ThreadColumns.ROOT,
                        EmailProvider.MessageColumns.SUBJECT,
                        EmailProvider.MessageColumns.DATE,
                        EmailProvider.SpecialColumns.THREAD_COUNT
                },
                "",
                new String[] {},
                EmailProvider.MessageColumns.DATE + " DESC");
        
        assertNotNull(cursor);
        assertTrue(cursor.moveToFirst());
        assertEquals(2, cursor.getInt(5));
        assertFalse(cursor.moveToNext());
    }

    @Test
    public void query_forAThreadOfMessages_returnsMessage() throws MessagingException {
        Account account = Preferences.getPreferences(getContext()).newAccount();
        account.getUuid();
        Message message = new MimeMessage();
        message.setSubject("Test Subject");
        message.setSentDate(new GregorianCalendar(2016, 1, 2).getTime(), false);
        account.getLocalStore().getFolder("Inbox").appendMessages(Collections.singletonList(message));

        //Now get the thread id we just put in.
        Cursor cursor = getProvider().query(
                Uri.parse("content://" + EmailProvider.AUTHORITY + "/account/" + account.getUuid() + "/messages"),
                new String[] {
                        EmailProvider.MessageColumns.ID,
                        EmailProvider.MessageColumns.FOLDER_ID,
                        EmailProvider.ThreadColumns.ROOT,
                },
                "",
                new String[] {},
                EmailProvider.MessageColumns.DATE);

        assertNotNull(cursor);
        cursor.moveToFirst();
        String threadId = cursor.getString(2);

        //Now check the message is listed under that thread
        Cursor threadCursor = getProvider().query(
                Uri.parse("content://" + EmailProvider.AUTHORITY + "/account/" + account.getUuid() +
                        "/thread/" + threadId),
                new String[] {
                        EmailProvider.MessageColumns.ID,
                        EmailProvider.MessageColumns.FOLDER_ID,
                        EmailProvider.ThreadColumns.ROOT,
                        EmailProvider.MessageColumns.SUBJECT,
                        EmailProvider.MessageColumns.DATE
                },
                "",
                new String[] {},
                EmailProvider.MessageColumns.DATE);

        assertNotNull(threadCursor);
        assertTrue(threadCursor.moveToFirst());
        assertEquals(message.getSubject(), threadCursor.getString(3));
    }
}
