package de.test.antennapod.ui;

import android.content.Context;
import android.util.Log;

import androidx.test.platform.app.InstrumentationRegistry;

import de.danoeh.antennapod.event.FeedListUpdateEvent;
import de.danoeh.antennapod.event.QueueEvent;
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.database.PodDBAdapter;
import de.test.antennapod.util.service.download.HTTPBin;
import de.test.antennapod.util.syndication.feedgenerator.Rss2Generator;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.greenrobot.eventbus.EventBus;
import org.junit.Assert;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

/**
 * Utility methods for UI tests.
 * Starts a web server that hosts feeds, episodes and images.
 */
public class UITestUtils {

    private static final String TAG = UITestUtils.class.getSimpleName();

    private static final int NUM_FEEDS = 5;
    private static final int NUM_ITEMS_PER_FEED = 10;

    private String testFileName = "3sec.mp3";
    private boolean hostTextOnlyFeeds = false;
    private final Context context;
    private final HTTPBin server = new HTTPBin();
    private File destDir;
    private File hostedFeedDir;
    private File hostedMediaDir;

    public final List<Feed> hostedFeeds = new ArrayList<>();

    public UITestUtils(Context context) {
        this.context = context;
    }


    public void setup() throws IOException {
        destDir = new File(context.getFilesDir(), "test/UITestUtils");
        destDir.mkdirs();
        hostedFeedDir = new File(destDir, "hostedFeeds");
        hostedFeedDir.mkdir();
        hostedMediaDir = new File(destDir, "hostedMediaDir");
        hostedMediaDir.mkdir();
        Assert.assertTrue(destDir.exists());
        Assert.assertTrue(hostedFeedDir.exists());
        Assert.assertTrue(hostedMediaDir.exists());
        server.start();
    }

    public void tearDown() throws IOException {
        FileUtils.deleteDirectory(destDir);
        FileUtils.deleteDirectory(hostedMediaDir);
        FileUtils.deleteDirectory(hostedFeedDir);
        server.stop();

        if (localFeedDataAdded) {
            PodDBAdapter.deleteDatabase();
        }
    }

    public String hostFeed(Feed feed) throws IOException {
        File feedFile = new File(hostedFeedDir, feed.getTitle());
        FileOutputStream out = new FileOutputStream(feedFile);
        Rss2Generator generator = new Rss2Generator();
        generator.writeFeed(feed, out, "UTF-8", 0);
        out.close();
        int id = server.serveFile(feedFile);
        Assert.assertTrue(id != -1);
        return String.format(Locale.US, "%s/files/%d", server.getBaseUrl(), id);
    }

    private String hostFile(File file) {
        int id = server.serveFile(file);
        Assert.assertTrue(id != -1);
        return String.format(Locale.US, "%s/files/%d", server.getBaseUrl(), id);
    }

    private File newMediaFile(String name) throws IOException {
        File mediaFile = new File(hostedMediaDir, name);
        if (mediaFile.exists()) {
            mediaFile.delete();
        }
        Assert.assertFalse(mediaFile.exists());

        InputStream in = InstrumentationRegistry.getInstrumentation().getContext()
                .getAssets().open(testFileName);
        Assert.assertNotNull(in);

        FileOutputStream out = new FileOutputStream(mediaFile);
        IOUtils.copy(in, out);
        out.close();

        return mediaFile;
    }

    private boolean feedDataHosted = false;

    /**
     * Adds feeds, images and episodes to the webserver for testing purposes.
     */
    public void addHostedFeedData() throws IOException {
        if (feedDataHosted) throw new IllegalStateException("addHostedFeedData was called twice on the same instance");
        for (int i = 0; i < NUM_FEEDS; i++) {
            Feed feed = new Feed(0, null, "Title " + i, "http://example.com/" + i, "Description of feed " + i,
                    "http://example.com/pay/feed" + i, "author " + i, "en", Feed.TYPE_RSS2, "feed" + i, null, null,
                    "http://example.com/feed/src/" + i, System.currentTimeMillis());

            // create items
            List<FeedItem> items = new ArrayList<>();
            for (int j = 0; j < NUM_ITEMS_PER_FEED; j++) {
                FeedItem item = new FeedItem(0, "Feed " + (i+1) + ": Item " + (j+1), "item" + j,
                        "http://example.com/feed" + i + "/item/" + j, new Date(), FeedItem.UNPLAYED, feed);
                items.add(item);

                if (!hostTextOnlyFeeds) {
                    File mediaFile = newMediaFile("feed-" + i + "-episode-" + j + ".mp3");
                    item.setMedia(new FeedMedia(j, item, 0, 0, mediaFile.length(), "audio/mp3",
                            null, hostFile(mediaFile), 0, null, 0, 0));
                }
            }
            feed.setItems(items);
            feed.setDownloadUrl(hostFeed(feed));
            hostedFeeds.add(feed);
        }
        feedDataHosted = true;
    }


    private boolean localFeedDataAdded = false;

    /**
     * Adds feeds, images and episodes to the local database. This method will also call addHostedFeedData if it has not
     * been called yet.
     *
     * Adds one item of each feed to the queue and to the playback history.
     *
     * This method should NOT be called if the testing class wants to download the hosted feed data.
     *
     * @param downloadEpisodes true if episodes should also be marked as downloaded.
     */
    public void addLocalFeedData(boolean downloadEpisodes) throws Exception {
        if (localFeedDataAdded) {
            Log.w(TAG, "addLocalFeedData was called twice on the same instance");
            // might be a flaky test, this is actually not that severe
            return;
        }
        if (!feedDataHosted) {
            addHostedFeedData();
        }

        List<FeedItem> queue = new ArrayList<>();
        for (Feed feed : hostedFeeds) {
            if (downloadEpisodes) {
                for (FeedItem item : feed.getItems()) {
                    if (item.hasMedia()) {
                        FeedMedia media = item.getMedia();
                        int fileId = Integer.parseInt(StringUtils.substringAfter(media.getDownloadUrl(), "files/"));
                        media.setLocalFileUrl(server.accessFile(fileId).getAbsolutePath());
                        media.setDownloaded(true, System.currentTimeMillis());
                    }
                }
            }

            queue.add(feed.getItems().get(0));
            if (feed.getItems().get(1).hasMedia()) {
                feed.getItems().get(1).getMedia().setLastPlayedTimeHistory(new Date());
            }
        }
        localFeedDataAdded = true;

        PodDBAdapter adapter = PodDBAdapter.getInstance();
        adapter.open();
        adapter.setCompleteFeed(hostedFeeds.toArray(new Feed[0]));
        adapter.setQueue(queue);
        adapter.close();
        EventBus.getDefault().post(new FeedListUpdateEvent(hostedFeeds));
        EventBus.getDefault().post(QueueEvent.setQueue(queue));
    }

    public void setMediaFileName(String filename) {
        testFileName = filename;
    }

    public void setHostTextOnlyFeeds(boolean hostTextOnlyFeeds) {
        this.hostTextOnlyFeeds = hostTextOnlyFeeds;
    }
}
