package de.danoeh.antennapod.ui.statistics.feed;

import android.os.Bundle;
import android.text.format.Formatter;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import de.danoeh.antennapod.storage.database.DBReader;
import de.danoeh.antennapod.storage.database.StatisticsItem;
import de.danoeh.antennapod.ui.common.Converter;
import de.danoeh.antennapod.ui.common.DateFormatter;
import de.danoeh.antennapod.storage.database.ReleaseScheduleGuesser;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedItemFilter;
import de.danoeh.antennapod.model.feed.SortOrder;
import de.danoeh.antennapod.ui.common.ThemeUtils;
import de.danoeh.antennapod.ui.statistics.R;
import de.danoeh.antennapod.ui.statistics.databinding.FeedStatisticsBinding;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;

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

public class FeedStatisticsFragment extends Fragment {
    private static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
    private static final String EXTRA_DETAILED = "de.danoeh.antennapod.extra.detailed";

    private long feedId;
    private Disposable disposable;
    private FeedStatisticsBinding viewBinding;

    public static FeedStatisticsFragment newInstance(long feedId, boolean detailed) {
        FeedStatisticsFragment fragment = new FeedStatisticsFragment();
        Bundle arguments = new Bundle();
        arguments.putLong(EXTRA_FEED_ID, feedId);
        arguments.putBoolean(EXTRA_DETAILED, detailed);
        fragment.setArguments(arguments);
        return fragment;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        feedId = getArguments().getLong(EXTRA_FEED_ID);
        viewBinding = FeedStatisticsBinding.inflate(inflater);
        loadStatistics();

        if (getArguments().getBoolean(EXTRA_DETAILED)) {
            viewBinding.secondRowContainer.setVisibility(View.VISIBLE);
            int color = ThemeUtils.getColorFromAttr(getContext(), R.attr.colorSurface);
            viewBinding.playbackTime.getRoot().setBackgroundColor(color);
            viewBinding.episodesStarted.getRoot().setBackgroundColor(color);
            viewBinding.spaceDownloaded.getRoot().setBackgroundColor(color);
            viewBinding.episodesTotal.getRoot().setBackgroundColor(color);
            viewBinding.durationTotal.getRoot().setBackgroundColor(color);
            viewBinding.episodesDownloaded.getRoot().setBackgroundColor(color);
            viewBinding.expectedNextEpisode.getRoot().setBackgroundColor(color);
            viewBinding.episodeSchedule.getRoot().setBackgroundColor(color);
        }
        return viewBinding.getRoot();
    }

    private void loadStatistics() {
        disposable =
                Observable.fromCallable(() -> {
                    DBReader.StatisticsResult statisticsData = DBReader.getStatistics(true, 0, Long.MAX_VALUE);
                    Collections.sort(statisticsData.feedTime, (item1, item2) ->
                            Long.compare(item2.timePlayed, item1.timePlayed));

                    for (StatisticsItem statisticsItem : statisticsData.feedTime) {
                        if (statisticsItem.feed.getId() == feedId) {
                            List<FeedItem> items = DBReader.getFeedItemList(statisticsItem.feed,
                                    FeedItemFilter.unfiltered(), SortOrder.DATE_OLD_NEW, 0, Integer.MAX_VALUE);
                            List<Date> dates = new ArrayList<>();
                            for (FeedItem item : items) {
                                dates.add(item.getPubDate());
                            }
                            ReleaseScheduleGuesser.Guess guess = null;
                            if (dates.size() > 1) {
                                guess = ReleaseScheduleGuesser.performGuess(dates);
                            }
                            return new Pair<>(statisticsItem, guess);
                        }
                    }
                    return null;
                })
                        .subscribeOn(Schedulers.computation())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe(this::showStats, Throwable::printStackTrace);
    }

    private String getReadableDay(int day) {
        switch (day) {
            case Calendar.MONDAY:
                return getString(R.string.release_schedule_monday);
            case Calendar.TUESDAY:
                return getString(R.string.release_schedule_tuesday);
            case Calendar.WEDNESDAY:
                return getString(R.string.release_schedule_wednesday);
            case Calendar.THURSDAY:
                return getString(R.string.release_schedule_thursday);
            case Calendar.FRIDAY:
                return getString(R.string.release_schedule_friday);
            case Calendar.SATURDAY:
                return getString(R.string.release_schedule_saturday);
            case Calendar.SUNDAY:
                return getString(R.string.release_schedule_sunday);
            default:
                return "error";
        }
    }

    private String getReadableSchedule(ReleaseScheduleGuesser.Guess guess) {
        String prefix = guess.multipleReleasesPerDay ? getString(R.string.release_schedule_multiple_per_day)
                + ", " : "";
        switch (guess.schedule) {
            case DAILY:
                return prefix + getString(R.string.release_schedule_daily);
            case WEEKDAYS:
                return prefix + getString(R.string.release_schedule_weekdays);
            case WEEKLY:
                return prefix + getString(R.string.release_schedule_weekly) + ", " + getReadableDay(guess.days.get(0));
            case BIWEEKLY:
                return prefix + getString(R.string.release_schedule_biweekly) + ", "
                        + getReadableDay(guess.days.get(0));
            case MONTHLY:
                return prefix + getString(R.string.release_schedule_monthly);
            case FOURWEEKLY:
                return prefix + getString(R.string.release_schedule_monthly) + ", " + getReadableDay(guess.days.get(0));
            case SPECIFIC_DAYS:
                StringBuilder days = new StringBuilder();
                for (int i = 0; i < guess.days.size(); i++) {
                    if (i != 0) {
                        days.append(", ");
                    }
                    days.append(getReadableDay(guess.days.get(i)));
                }
                return prefix + days.toString();
            default:
                return prefix + getString(R.string.statistics_expected_next_episode_unknown);
        }
    }

    private void showStats(Pair<StatisticsItem, ReleaseScheduleGuesser.Guess> p) {
        StatisticsItem s = p.first;
        viewBinding.episodesStarted.mainLabel.setText(getResources()
                .getQuantityString(R.plurals.num_episodes, (int) s.episodesStarted, s.episodesStarted));
        viewBinding.episodesStarted.subtitleLabel.setText(getResources()
                .getQuantityString(R.plurals.statistics_episodes_started, (int) s.episodesStarted));

        viewBinding.episodesTotal.mainLabel.setText(getResources()
                .getQuantityString(R.plurals.num_episodes, (int) s.episodes, s.episodes));
        viewBinding.episodesTotal.subtitleLabel.setText(getResources()
                .getQuantityString(R.plurals.statistics_episodes_total, (int) s.episodes));

        viewBinding.playbackTime.mainLabel.setText(Converter.shortLocalizedDuration(getContext(), s.timePlayed));
        viewBinding.playbackTime.subtitleLabel.setText(R.string.statistics_time_played);

        viewBinding.durationTotal.mainLabel.setText(Converter.shortLocalizedDuration(getContext(), s.time));
        viewBinding.durationTotal.subtitleLabel.setText(R.string.statistics_time_total);

        viewBinding.episodesDownloaded.mainLabel.setText(getResources()
                .getQuantityString(R.plurals.num_episodes, (int) s.episodesDownloadCount, s.episodesDownloadCount));
        viewBinding.episodesDownloaded.subtitleLabel.setText(getResources()
                .getQuantityString(R.plurals.statistics_episodes_downloaded, (int) s.episodesDownloadCount));

        viewBinding.spaceDownloaded.mainLabel.setText(Formatter.formatShortFileSize(getContext(), s.totalDownloadSize));
        viewBinding.spaceDownloaded.subtitleLabel.setText(R.string.statistics_episodes_space);

        viewBinding.expectedNextEpisode.subtitleLabel.setText(R.string.statistics_release_next);
        viewBinding.episodeSchedule.subtitleLabel.setText(R.string.statistics_release_schedule);
        ReleaseScheduleGuesser.Guess guess = p.second;
        if (s.feed.isLocalFeed()) {
            viewBinding.expectedNextEpisode.mainLabel.setText(R.string.local_folder);
            viewBinding.episodeSchedule.mainLabel.setText(R.string.local_folder);
        } else if (!s.feed.getPreferences().getKeepUpdated()) {
            viewBinding.expectedNextEpisode.mainLabel.setText(R.string.updates_disabled_label);
            viewBinding.episodeSchedule.mainLabel.setText(R.string.updates_disabled_label);
        } else if (guess == null || guess.nextExpectedDate.getTime() <= new Date().getTime() - 7 * 24 * 3600000L) {
            // More than 30 days delayed
            viewBinding.expectedNextEpisode.mainLabel.setText(R.string.statistics_expected_next_episode_unknown);
            viewBinding.episodeSchedule.mainLabel.setText(R.string.statistics_expected_next_episode_unknown);
        } else {
            if (guess.nextExpectedDate.getTime() <= new Date().getTime()) {
                viewBinding.expectedNextEpisode.mainLabel.setText(
                        guess.multipleReleasesPerDay
                                ? R.string.statistics_expected_next_episode_any_time
                                : R.string.statistics_expected_next_episode_any_day);
            } else {
                viewBinding.expectedNextEpisode.mainLabel.setText(
                        DateFormatter.formatAbbrev(getContext(), guess.nextExpectedDate));
            }
            if (guess.schedule == ReleaseScheduleGuesser.Schedule.UNKNOWN) {
                viewBinding.episodeSchedule.mainLabel.setText(R.string.statistics_expected_next_episode_unknown);
            } else {
                viewBinding.episodeSchedule.mainLabel.setText(getReadableSchedule(guess));
            }
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (disposable != null) {
            disposable.dispose();
        }
    }
}
