package allen.town.podcast.core.service.download;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.os.Build;
import android.util.Log;
import androidx.core.app.NotificationCompat;
import allen.town.podcast.core.ClientConfig;
import allen.town.podcast.core.R;
import allen.town.podcast.core.util.DownloadErrorLabel;
import allen.town.podcast.model.download.DownloadStatus;
import allen.town.podcast.model.feed.Feed;
import allen.town.podcast.model.feed.FeedMedia;
import allen.town.podcast.core.util.ui.NotificationUtils;

import java.util.List;

public class DownloadServiceNotification {
    private static final String TAG = "DownloadSvcNotification";

    private final Context context;
    private NotificationCompat.Builder notificationCompatBuilder;

    public DownloadServiceNotification(Context context) {
        this.context = context;
        setupNotificationBuilders();
    }

    private void setupNotificationBuilders() {
        notificationCompatBuilder = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID_DOWNLOADING)
                .setOngoing(false)
                .setWhen(0)
                .setOnlyAlertOnce(true)
                .setShowWhen(false)
                .setContentIntent(ClientConfig.downloadServiceCallbacks.getNotificationContentIntent(context))
                .setSmallIcon(R.drawable.ic_notification_sync);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            notificationCompatBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
        }

    }

    /**
     * Updates the contents of the service's notifications. Should be called
     * after setupNotificationBuilders.
     */
    public Notification updateNotifications(List<Downloader> downloads) {
        if (notificationCompatBuilder == null) {
            return null;
        }

        String contentTitle;
        if (typeIsOnly(downloads, Feed.FEEDFILETYPE_FEED)) {
            contentTitle = context.getString(R.string.download_notification_title_feeds);
        } else if (typeIsOnly(downloads, FeedMedia.FEEDFILETYPE_FEEDMEDIA)) {
            contentTitle = context.getString(R.string.download_notification_title_episodes);
            notificationCompatBuilder.setSmallIcon(R.drawable.ic_download);
        } else {
            contentTitle = context.getString(R.string.download_notification_title);
        }
        String contentText = (downloads.size() > 0)
                ? context.getResources().getQuantityString(R.plurals.downloads_left, downloads.size(), downloads.size())
                : context.getString(R.string.completing);
        String bigText = compileNotificationString(downloads);
        if (!bigText.contains("\n")) {
            contentText = bigText;
        }

        notificationCompatBuilder.setContentTitle(contentTitle);
        notificationCompatBuilder.setContentText(contentText);
        notificationCompatBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(bigText));
        return notificationCompatBuilder.build();
    }

    private boolean typeIsOnly(List<Downloader> downloads, int feedFileType) {
        for (Downloader downloader : downloads) {
            if (downloader.cancelled) {
                continue;
            }
            DownloadRequest request = downloader.getDownloadRequest();
            if (request.getFeedfileType() != feedFileType) {
                return false;
            }
        }
        return true;
    }

    private static String compileNotificationString(List<Downloader> downloads) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < downloads.size(); i++) {
            Downloader downloader = downloads.get(i);
            if (downloader.cancelled) {
                continue;
            }
            DownloadRequest request = downloader.getDownloadRequest();
            if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
                stringBuilder.append(" [").append(request.getProgressPercent()).append("%] ");
            } else if (request.getSource().startsWith(Feed.PREFIX_LOCAL_FOLDER)) {
                stringBuilder.append(" [").append(request.getSoFar())
                        .append("/").append(request.getSize()).append("] ");
            }
            if (request.getTitle() != null) {
                stringBuilder.append(request.getTitle());
            } else {
                stringBuilder.append(request.getSource());
            }

            if (i != downloads.size() - 1) {
                stringBuilder.append("\n");
            }
        }
        return stringBuilder.toString();
    }

    private static String createAutoDownloadNotificationContent(List<DownloadStatus> statuses) {
        int length = statuses.size();
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < length; i++) {
            sb.append("• ").append(statuses.get(i).getTitle());
            if (i != length - 1) {
                sb.append("\n");
            }
        }

        return sb.toString();
    }

    private String createFailedDownloadNotificationContent(List<DownloadStatus> statuses) {
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < statuses.size(); i++) {
            if (statuses.get(i) == null || statuses.get(i).isSuccessful()) {
                continue;
            }
            sb.append("• ").append(statuses.get(i).getTitle());
            if (statuses.get(i).getReason() != null) {
                sb.append(": ").append(context.getString(DownloadErrorLabel.from(statuses.get(i).getReason())));
            }
            if (i != statuses.size() - 1) {
                sb.append("\n");
            }
        }

        return sb.toString();
    }

    /**
     * Creates a notification at the end of the service lifecycle to notify the
     * user about the number of completed downloads. A report will only be
     * created if there is at least one failed download excluding images
     */
    public void updateReport(List<DownloadStatus> reportQueue, boolean showAutoDownloadReport) {
        // check if report should be created
        boolean createReport = false;
        int failedDownloads = 0;

        // a download report is created if at least one download has failed
        // (excluding failed image downloads)
        for (DownloadStatus status : reportQueue) {
            if (status == null || status.isCancelled()) {
                continue;
            }
            if (status.isSuccessful()) {
                createReport |= showAutoDownloadReport && !status.isInitiatedByUser()
                        && status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA;
            } else {
                failedDownloads++;
                createReport = true;
            }
        }

        if (createReport) {

            // create notification object
            String channelId;
            int titleId;
            int iconId;
            int id;
            String content;
            PendingIntent intent;
            if (failedDownloads == 0) {
                // We are generating an auto-download report
                channelId = NotificationUtils.CHANNEL_ID_AUTO_DOWNLOAD;
                titleId = R.string.auto_download_report_title;
                iconId = R.drawable.ic_notification_new;
                intent = ClientConfig.downloadServiceCallbacks.getAutoDownloadReportNotificationContentIntent(context);
                id = R.id.notification_auto_download_report;
                content = createAutoDownloadNotificationContent(reportQueue);
            } else {
                channelId = NotificationUtils.CHANNEL_ID_DOWNLOAD_ERROR;
                titleId = R.string.download_report_title;
                iconId = R.drawable.ic_notification_sync_error;
                intent = ClientConfig.downloadServiceCallbacks.getReportNotificationContentIntent(context);
                id = R.id.notification_download_report;
                content = createFailedDownloadNotificationContent(reportQueue);
            }

            NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId);
            builder.setTicker(context.getString(titleId))
                    .setContentTitle(context.getString(titleId))
                    .setContentText(content)
                    .setStyle(new NotificationCompat.BigTextStyle().bigText(content))
                    .setSmallIcon(iconId)
                    .setContentIntent(intent)
                    .setAutoCancel(true);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
            }
            NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            nm.notify(id, builder.build());
            Log.d(TAG, "push download report notification");
        } else {
            Log.d(TAG, "no report is created");
        }
    }

    public void postAuthenticationNotification(final DownloadRequest downloadRequest) {
        final String resourceTitle = (downloadRequest.getTitle() != null) ?
                downloadRequest.getTitle() : downloadRequest.getSource();

        NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID_USER_ACTION);
        builder.setTicker(context.getText(R.string.authentication_notification_title))
                .setContentTitle(context.getText(R.string.authentication_notification_title))
                .setContentText(context.getText(R.string.authentication_notification_msg))
                .setStyle(new NotificationCompat.BigTextStyle().bigText(context.getText(R.string.authentication_notification_msg)
                        + ": " + resourceTitle))
                .setSmallIcon(R.drawable.ic_notification_key)
                .setAutoCancel(true)
                .setContentIntent(ClientConfig.downloadServiceCallbacks.getAuthentificationNotificationContentIntent(context, downloadRequest));
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
        }
        NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        nm.notify(downloadRequest.getSource().hashCode(), builder.build());
    }
}
