package de.danoeh.antennapod.storage.database;

import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import de.danoeh.antennapod.model.feed.SortOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.storage.preferences.UserPreferences;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

/**
 * Test class for FeedItemPermutors.
 */
@RunWith(RobolectricTestRunner.class)
public class FeedItemPermutorsTest {

    @Test
    public void testEnsureNonNullPermutors() {
        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
        UserPreferences.init(context);
        for (SortOrder sortOrder : SortOrder.values()) {
            assertNotNull("The permutor for SortOrder " + sortOrder + " is unexpectedly null",
                    FeedItemPermutors.getPermutor(sortOrder));
        }
    }

    @Test
    public void testPermutorForRule_EPISODE_TITLE_ASC() {
        Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.EPISODE_TITLE_A_Z);

        List<FeedItem> itemList = getTestList();
        assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
        permutor.reorder(itemList);
        assertTrue(checkIdOrder(itemList, 1, 2, 3)); // after sorting
    }

    @Test
    public void testPermutorForRule_EPISODE_TITLE_ASC_NullTitle() {
        Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.EPISODE_TITLE_A_Z);

        List<FeedItem> itemList = getTestList();
        itemList.get(2) // itemId 2
                .setTitle(null);
        assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
        permutor.reorder(itemList);
        assertTrue(checkIdOrder(itemList, 2, 1, 3)); // after sorting
    }


    @Test
    public void testPermutorForRule_EPISODE_TITLE_DESC() {
        Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.EPISODE_TITLE_Z_A);

        List<FeedItem> itemList = getTestList();
        assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
        permutor.reorder(itemList);
        assertTrue(checkIdOrder(itemList, 3, 2, 1)); // after sorting
    }

    @Test
    public void testPermutorForRule_DATE_ASC() {
        Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.DATE_OLD_NEW);

        List<FeedItem> itemList = getTestList();
        assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
        permutor.reorder(itemList);
        assertTrue(checkIdOrder(itemList, 1, 2, 3)); // after sorting
    }

    @Test
    public void testPermutorForRule_DATE_ASC_NulPubDatel() {
        Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.DATE_OLD_NEW);

        List<FeedItem> itemList = getTestList();
        itemList.get(2) // itemId 2
                .setPubDate(null);
        assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
        permutor.reorder(itemList);
        assertTrue(checkIdOrder(itemList, 2, 1, 3)); // after sorting
    }

    @Test
    public void testPermutorForRule_DATE_DESC() {
        Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.DATE_NEW_OLD);

        List<FeedItem> itemList = getTestList();
        assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
        permutor.reorder(itemList);
        assertTrue(checkIdOrder(itemList, 3, 2, 1)); // after sorting
    }

    @Test
    public void testPermutorForRule_DURATION_ASC() {
        Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.DURATION_SHORT_LONG);

        List<FeedItem> itemList = getTestList();
        assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
        permutor.reorder(itemList);
        assertTrue(checkIdOrder(itemList, 1, 2, 3)); // after sorting
    }

    @Test
    public void testPermutorForRule_DURATION_DESC() {
        Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.DURATION_LONG_SHORT);

        List<FeedItem> itemList = getTestList();
        assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
        permutor.reorder(itemList);
        assertTrue(checkIdOrder(itemList, 3, 2, 1)); // after sorting
    }

    @Test
    public void testPermutorForRule_size_asc() {
        Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.SIZE_SMALL_LARGE);

        List<FeedItem> itemList = getTestList();
        assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
        permutor.reorder(itemList);
        assertTrue(checkIdOrder(itemList, 1, 2, 3)); // after sorting
    }

    @Test
    public void testPermutorForRule_size_desc() {
        Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.SIZE_LARGE_SMALL);

        List<FeedItem> itemList = getTestList();
        assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
        permutor.reorder(itemList);
        assertTrue(checkIdOrder(itemList, 3, 2, 1)); // after sorting
    }

    @Test
    public void testPermutorForRule_DURATION_DESC_NullMedia() {
        Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.DURATION_LONG_SHORT);

        List<FeedItem> itemList = getTestList();
        itemList.get(1) // itemId 3
                .setMedia(null);
        assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
        permutor.reorder(itemList);
        assertTrue(checkIdOrder(itemList, 2, 1, 3)); // after sorting
    }

    @Test
    public void testPermutorForRule_FEED_TITLE_ASC() {
        Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.FEED_TITLE_A_Z);

        List<FeedItem> itemList = getTestList();
        assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
        permutor.reorder(itemList);
        assertTrue(checkIdOrder(itemList, 1, 2, 3)); // after sorting
    }

    @Test
    public void testPermutorForRule_FEED_TITLE_DESC() {
        Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.FEED_TITLE_Z_A);

        List<FeedItem> itemList = getTestList();
        assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
        permutor.reorder(itemList);
        assertTrue(checkIdOrder(itemList, 3, 2, 1)); // after sorting
    }

    @Test
    public void testPermutorForRule_FEED_TITLE_DESC_NullTitle() {
        Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(SortOrder.FEED_TITLE_Z_A);

        List<FeedItem> itemList = getTestList();
        itemList.get(1) // itemId 3
            .getFeed().setTitle(null);
        assertTrue(checkIdOrder(itemList, 1, 3, 2)); // before sorting
        permutor.reorder(itemList);
        assertTrue(checkIdOrder(itemList, 2, 1, 3)); // after sorting
    }

    /**
     * Generates a list with test data.
     */
    private List<FeedItem> getTestList() {
        List<FeedItem> itemList = new ArrayList<>();

        Calendar calendar = Calendar.getInstance();
        calendar.set(2019, 0, 1);  // January 1st
        Feed feed1 = new Feed(null, null, "Feed title 1");
        FeedItem feedItem1 = new FeedItem(1, "Title 1", null, null, calendar.getTime(), 0, feed1);
        FeedMedia feedMedia1 = new FeedMedia(0, feedItem1, 1000, 0, 100, null, null, null,
                System.currentTimeMillis(), null, 0, 0);
        feedItem1.setMedia(feedMedia1);
        itemList.add(feedItem1);

        calendar.set(2019, 2, 1);  // March 1st
        Feed feed2 = new Feed(null, null, "Feed title 3");
        FeedItem feedItem2 = new FeedItem(3, "Title 3", null, null, calendar.getTime(), 0, feed2);
        FeedMedia feedMedia2 = new FeedMedia(0, feedItem2, 3000, 0, 300, null, null, null,
                System.currentTimeMillis(), null, 0, 0);
        feedItem2.setMedia(feedMedia2);
        itemList.add(feedItem2);

        calendar.set(2019, 1, 1);  // February 1st
        Feed feed3 = new Feed(null, null, "Feed title 2");
        FeedItem feedItem3 = new FeedItem(2, "Title 2", null, null, calendar.getTime(), 0, feed3);
        FeedMedia feedMedia3 = new FeedMedia(0, feedItem3, 2000, 0, 200, null, null, null,
                System.currentTimeMillis(), null, 0, 0);
        feedItem3.setMedia(feedMedia3);
        itemList.add(feedItem3);

        return itemList;
    }

    /**
     * Checks if both lists have the same size and the same ID order.
     *
     * @param itemList Item list.
     * @param ids      List of IDs.
     * @return <code>true</code> if both lists have the same size and the same ID order.
     */
    private boolean checkIdOrder(List<FeedItem> itemList, long... ids) {
        if (itemList.size() != ids.length) {
            return false;
        }

        for (int i = 0; i < ids.length; i++) {
            if (itemList.get(i).getId() != ids[i]) {
                return false;
            }
        }
        return true;
    }
}
