package com.castlabs.sdk.downloads;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.app.TaskStackBuilder;
import android.util.Log;

import com.castlabs.sdk.downloader.Download;
import com.castlabs.sdk.downloader.DownloadNotificationProvider;
import com.castlabs.sdk.downloader.DownloadServiceBinder;
import com.castlabs.sdk.downloader.MessageHandler;

import java.util.List;

public class NotificationProvider extends DownloadNotificationProvider {

	private static final String TAG = "NotificationProvider";

	private static final int DOWNLOAD_NOTIFICATION_ID = 1234;
	private String lastAction = "";
	private Notification.Builder builder;
	private boolean storageLow = false;

	public NotificationProvider() {
		super(DOWNLOAD_NOTIFICATION_ID);
	}

	@NonNull
	public Notification getNotification(@NonNull DownloadServiceBinder downloadServiceBinder, @NonNull Context context) {
		// Setup the notification builder. We enable channel support on O
		if (builder == null) {
			String id = "content_download_channel";
			if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
				createNotificationChannel(context, id);
				builder = new Notification.Builder(context, id);
			} else {
				builder = new Notification.Builder(context);
			}
		}

		// Add notification action when the user taps the notification
		Intent i = new Intent(context, MainActivity.class);
		TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
		stackBuilder.addNextIntent(i);
		PendingIntent pendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
		builder.setContentIntent(pendingIntent);

		// Set the basic properties of the notification
		builder.setAutoCancel(false)
				.setSmallIcon(R.drawable.notofications_icon);


		List<Download> downloads;
		try {
			downloads = downloadServiceBinder.getDownloads();
		} catch (Exception e) {
			Log.e(TAG, "Error while getting Downloads", e);
			return builder.build();
		}

		Download activeDownload = getActiveDownload(downloads);
		if (activeDownload != null) {
			builder.setContentTitle("Downloading #" + activeDownload.getId());
			builder.setContentText("");

			int waitingDownloads = getWaitingDownloads(downloads);
			if (waitingDownloads > 1) {
				builder.setContentText(waitingDownloads
						+ " more " + ((waitingDownloads - 1) == 1 ? "is" : "are") +
						" still in the queue.");
			} else {
				builder.setContentText("");
			}
			double downloadedRatio = (double) activeDownload.getDownloadedSize() / (double) activeDownload.getEstimatedSize();
			int progress = Math.max(0, Math.min(100, (int) (downloadedRatio * 100.0)));
			builder.setProgress(100, progress, false);
			// Set the state to ongoing so the notification can not be removed by the user since
			// we need to notification in place while downloading. This is especially important
			// since Android O because only the notification allows us to keep the download service
			// running even if the App is in background
			builder.setOngoing(true);
		} else {
			// If we have no currently active download we can mark the notification as not-ongoing
			// and allow the user to remove it
			builder.setOngoing(false);
			// Make sure the progress indicator is hidden
			builder.setProgress(0, 0, false);
			// Build a description
			if (storageLow) {
				// if the last action that we got was a storage low message, inform the user
				// that all downloads are paused because we do not have enough space.
				builder.setContentTitle("Not enough space!");
				builder.setContentText("Free up space to continue downloading");
			} else {
				int waitingDownloads = getWaitingDownloads(downloads);
				if (waitingDownloads == 0) {
					builder.setContentTitle("All Downloads completed");
					builder.setContentText("");
				} else {
					builder.setContentTitle("No Active Downloads");
					builder.setContentText(waitingDownloads
							+ " " + (waitingDownloads == 1 ? "is" : "are") +
							" still in the queued.");
				}
			}
		}
		return builder.build();
	}

	/**
	 * @param downloads The list of downloads
	 * @return The currently loading download or null
	 */
	private Download getActiveDownload(List<Download> downloads) {
		for (Download download : downloads) {
			if (download.getState() == Download.STATE_LOADING) {
				return download;
			}
		}
		return null;
	}

	/**
	 * @param downloads The list of downloads
	 * @return The number of not yet completed downloads
	 */
	private int getWaitingDownloads(List<Download> downloads) {
		int count = 0;
		for (Download download : downloads) {
			if (download.getState() != Download.STATE_DONE) {
				count++;
			}
		}
		return count;
	}

	@Override
	public boolean shouldKeepNotification(@NonNull DownloadServiceBinder downloadServiceBinder) {
		// The default implementation returns false here, which means that
		// notification will be removed when the download service has no pending downloads
		// and leaves foreground mode.
		// We return true here to keep our notifications displayed and let the user actively dismiss
		// them. Note that we make sure to call setOngoing accordingly when we build our notifications
		// to prevent the user from removing a notification while downloads are still in
		// progress
		return true;
	}

	@Override
	public boolean onDownloadEvent(@NonNull DownloadServiceBinder downloadServiceBinder, @NonNull Intent intent) {
		// we get the event action here and cache it. This method is called before the
		// notification is created so we can use that when building the notification above
		lastAction = intent.getAction();
		if (lastAction == null) return false;

		// We can return false from this method if we don't want a notification to be shown.
		// Here we want to make sure that we do not show a notification when
		// we are deleting downloads. In call other cases we want the notification
		// to be shown to the user.
		switch (lastAction) {
			case MessageHandler.ACTION_DOWNLOAD_DELETED:
				return false;
			case MessageHandler.ACTION_DOWNLOAD_STORAGE_LOW:
				storageLow = true;
				break;
			case MessageHandler.ACTION_DOWNLOAD_STORAGE_OK:
				storageLow = false;
				break;
		}
		return true;
	}

	@RequiresApi(api = Build.VERSION_CODES.O)
	private static void createNotificationChannel(@NonNull Context context, @NonNull String id) {

		CharSequence name = "Downloads";
		String description = "These notifications will inform you about the ongoing downloads";

		NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
		if (notificationManager != null) {
			int importance = NotificationManager.IMPORTANCE_LOW;
			NotificationChannel channel = new NotificationChannel(id, name, importance);
			channel.setDescription(description);
			channel.enableLights(false);
			channel.enableVibration(false);
			notificationManager.createNotificationChannel(channel);
		}
	}
}
