/*
 * Copyright (c) 2016 Qiscus.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.qiscus.sdk.util;

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.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.media.RingtoneManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.app.RemoteInput;
import androidx.core.content.ContextCompat;
import androidx.core.util.Pair;
import android.text.TextUtils;

import com.bumptech.glide.request.target.SimpleTarget;
import com.bumptech.glide.request.transition.Transition;
import com.qiscus.nirmana.Nirmana;
import com.qiscus.sdk.Qiscus;
import com.qiscus.sdk.R;
import com.qiscus.sdk.chat.core.data.local.QiscusCacheManager;
import com.qiscus.sdk.chat.core.data.model.QiscusChatRoom;
import com.qiscus.sdk.chat.core.data.model.QiscusComment;
import com.qiscus.sdk.chat.core.data.model.QiscusPushNotificationMessage;
import com.qiscus.sdk.chat.core.data.model.QiscusRoomMember;
import com.qiscus.sdk.chat.core.data.remote.QiscusApi;
import com.qiscus.sdk.chat.core.util.BuildVersionUtil;
import com.qiscus.sdk.chat.core.util.QiscusAndroidUtil;
import com.qiscus.sdk.chat.core.util.QiscusNumberUtil;
import com.qiscus.sdk.chat.core.util.QiscusRawDataExtractor;
import com.qiscus.sdk.chat.core.util.QiscusTextUtil;
import com.qiscus.sdk.data.model.QiscusMentionConfig;
import com.qiscus.sdk.service.QiscusPushNotificationClickReceiver;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;

import static com.qiscus.sdk.chat.core.util.BuildVersionUtil.isNougatOrHigher;

/**
 * Created on : June 15, 2017
 * Author     : zetbaitsu
 * Name       : Zetra
 * GitHub     : https://github.com/zetbaitsu
 */
public final class QiscusPushNotificationUtil {
    public static final String KEY_NOTIFICATION_REPLY = "KEY_NOTIFICATION_REPLY";

    public static void handlePushNotification(Context context, QiscusComment qiscusComment) {
        QiscusAndroidUtil.runOnBackgroundThread(() -> handlePN(context, qiscusComment));
    }

    public static void handleDeletedCommentNotification(Context context, List<QiscusComment> comments, boolean hardDelete) {
        QiscusAndroidUtil.runOnBackgroundThread(() -> handleDeletedComment(context, comments, hardDelete));
    }

    private static void handlePN(Context context, QiscusComment qiscusComment) {
        if (Qiscus.getDataStore().isContains(qiscusComment)) {
            return;
        }

        Qiscus.getDataStore().addOrUpdate(qiscusComment);

        Pair<Boolean, Long> lastChatActivity = QiscusCacheManager.getInstance().getLastChatActivity();
        if (!lastChatActivity.first || lastChatActivity.second != qiscusComment.getRoomId()) {
            updateUnreadCount(qiscusComment);
        }

        if (Qiscus.getChatConfig().isEnablePushNotification()
                && !qiscusComment.getSenderEmail().equalsIgnoreCase(Qiscus.getQiscusAccount().getEmail())) {
            if (Qiscus.getChatConfig().isOnlyEnablePushNotificationOutsideChatRoom()) {
                if (!lastChatActivity.first || lastChatActivity.second != qiscusComment.getRoomId()) {
                    showPushNotification(context, qiscusComment);
                }
            } else {
                showPushNotification(context, qiscusComment);
            }
        }
    }

    private static void updateUnreadCount(QiscusComment qiscusComment) {
        QiscusChatRoom room = Qiscus.getDataStore().getChatRoom(qiscusComment.getRoomId());
        if (room == null) {
            fetchRoomData(qiscusComment.getRoomId());
            return;
        }

        if (qiscusComment.isMyComment()) {
            room.setUnreadCount(0);
        } else {
            room.setUnreadCount(room.getUnreadCount() + 1);
        }
        Qiscus.getDataStore().addOrUpdate(room);
    }

    private static void fetchRoomData(long roomId) {
        QiscusApi.getInstance()
                .getChatRoom(roomId)
                .doOnNext(qiscusChatRoom -> Qiscus.getDataStore().addOrUpdate(qiscusChatRoom))
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(qiscusChatRoom -> {
                }, throwable -> {
                });
    }

    private static void showPushNotification(Context context, QiscusComment comment) {
        String messageText = comment.isGroupMessage() ? comment.getSender().split(" ")[0] + ": " : "";
        if (comment.getType() == QiscusComment.Type.SYSTEM_EVENT) {
            messageText = "";
        }

        QiscusMentionConfig mentionConfig = Qiscus.getChatConfig().getMentionConfig();
        Map<String, QiscusRoomMember> members = new HashMap<>();
        if (mentionConfig.isEnableMention()) {
            QiscusChatRoom room = Qiscus.getDataStore().getChatRoom(comment.getRoomId());
            if (room != null) {
                for (QiscusRoomMember member : room.getMember()) {
                    members.put(member.getEmail(), member);
                }
            }
        }

        switch (comment.getType()) {
            case IMAGE:
                if (mentionConfig.isEnableMention()) {
                    messageText += "\uD83D\uDCF7 " + (TextUtils.isEmpty(comment.getCaption()) ?
                            QiscusTextUtil.getString(R.string.qiscus_send_a_photo) :
                            new QiscusSpannableBuilder(comment.getCaption(), members).build().toString());
                } else {
                    messageText += "\uD83D\uDCF7 " + (TextUtils.isEmpty(comment.getCaption()) ?
                            QiscusTextUtil.getString(R.string.qiscus_send_a_photo) : comment.getCaption());
                }
                break;
            case VIDEO:
                if (mentionConfig.isEnableMention()) {
                    messageText += "\uD83C\uDFA5 " + (TextUtils.isEmpty(comment.getCaption()) ?
                            QiscusTextUtil.getString(R.string.qiscus_send_a_video) :
                            new QiscusSpannableBuilder(comment.getCaption(), members).build().toString());
                } else {
                    messageText += "\uD83C\uDFA5 " + (TextUtils.isEmpty(comment.getCaption()) ?
                            QiscusTextUtil.getString(R.string.qiscus_send_a_video) : comment.getCaption());
                }
                break;
            case AUDIO:
                messageText += "\uD83D\uDD0A " + QiscusTextUtil.getString(R.string.qiscus_send_a_audio);
                break;
            case CONTACT:
                messageText += "\u260E " + QiscusTextUtil.getString(R.string.qiscus_contact) + ": " +
                        comment.getContact().getName();
                break;
            case LOCATION:
                messageText += "\uD83D\uDCCD " + comment.getMessage();
                break;
            case CAROUSEL:
                try {
                    JSONObject payload = QiscusRawDataExtractor.getPayload(comment);
                    JSONArray cards = payload.optJSONArray("cards");
                    if (cards.length() > 0) {
                        messageText += "\uD83D\uDCDA " + cards.optJSONObject(0).optString("title");
                    } else {
                        messageText += "\uD83D\uDCDA " + QiscusTextUtil.getString(R.string.qiscus_send_a_carousel);
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                    messageText += "\uD83D\uDCDA " + QiscusTextUtil.getString(R.string.qiscus_send_a_carousel);
                }
                break;
            default:
                if (mentionConfig.isEnableMention()) {
                    messageText += comment.isAttachment() ? "\uD83D\uDCC4 " +
                            QiscusTextUtil.getString(R.string.qiscus_send_attachment) :
                            new QiscusSpannableBuilder(comment.getMessage(), members).build().toString();
                } else {
                    messageText += comment.isAttachment() ? "\uD83D\uDCC4 " +
                            QiscusTextUtil.getString(R.string.qiscus_send_attachment) : comment.getMessage();
                }
                break;
        }

        QiscusPushNotificationMessage pushNotificationMessage =
                new QiscusPushNotificationMessage(comment.getId(), messageText);
        pushNotificationMessage.setRoomName(comment.getRoomName());
        pushNotificationMessage.setRoomAvatar(comment.getRoomAvatar());
        if (!QiscusCacheManager.getInstance()
                .addMessageNotifItem(pushNotificationMessage, comment.getRoomId())) {
            return;
        }

        if (Qiscus.getChatConfig().isEnableAvatarAsNotificationIcon()) {
            QiscusAndroidUtil.runOnUIThread(() -> loadAvatar(context, comment, pushNotificationMessage));
        } else {
            pushNotification(context, comment, pushNotificationMessage,
                    BitmapFactory.decodeResource(context.getResources(), Qiscus.getChatConfig().getNotificationBigIcon()));
        }
    }

    private static void loadAvatar(Context context, QiscusComment comment, QiscusPushNotificationMessage pushNotificationMessage) {
        Nirmana.getInstance().get()
                .asBitmap()
                .load(pushNotificationMessage.getRoomAvatar())
                .into(new SimpleTarget<Bitmap>() {
                    @Override
                    public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
                        QiscusAndroidUtil.runOnBackgroundThread(() -> {
                            try {
                                pushNotification(context, comment, pushNotificationMessage, QiscusImageUtil.getCircularBitmap(resource));
                            } catch (Exception e) {
                                pushNotification(context, comment, pushNotificationMessage,
                                        BitmapFactory.decodeResource(context.getResources(),
                                                Qiscus.getChatConfig().getNotificationBigIcon()));
                            }
                        });
                    }

                    @Override
                    public void onLoadFailed(@Nullable Drawable errorDrawable) {
                        super.onLoadFailed(errorDrawable);
                        QiscusAndroidUtil.runOnBackgroundThread(() -> pushNotification(context, comment, pushNotificationMessage,
                                BitmapFactory.decodeResource(context.getResources(),
                                        Qiscus.getChatConfig().getNotificationBigIcon())));
                    }
                });
    }

    private static void pushNotification(Context context, QiscusComment comment,
                                         QiscusPushNotificationMessage pushNotificationMessage, Bitmap largeIcon) {

        String notificationChannelId = Qiscus.getApps().getPackageName() + ".qiscus.sdk.notification.channel";
        if (BuildVersionUtil.isOreoOrHigher()) {
            NotificationChannel notificationChannel =
                    new NotificationChannel(notificationChannelId, "Chat", NotificationManager.IMPORTANCE_HIGH);
            NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            if (notificationManager != null) {
                notificationManager.createNotificationChannel(notificationChannel);
            }
        }

        PendingIntent pendingIntent;
        Intent openIntent = new Intent(context, QiscusPushNotificationClickReceiver.class);
        openIntent.putExtra("data", comment);
        pendingIntent = PendingIntent.getBroadcast(context, QiscusNumberUtil.convertToInt(comment.getRoomId()),
                openIntent, PendingIntent.FLAG_CANCEL_CURRENT);

        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, notificationChannelId);
        notificationBuilder.setContentTitle(pushNotificationMessage.getRoomName())
                .setContentIntent(pendingIntent)
                .setContentText(pushNotificationMessage.getMessage())
                .setTicker(pushNotificationMessage.getMessage())
                .setSmallIcon(Qiscus.getChatConfig().getNotificationSmallIcon())
                .setLargeIcon(largeIcon)
                .setColor(ContextCompat.getColor(context, Qiscus.getChatConfig().getInlineReplyColor()))
                .setGroup("CHAT_NOTIF_" + comment.getRoomId())
                .setAutoCancel(true)
                .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));

        if (Qiscus.getChatConfig().isEnableReplyNotification() && isNougatOrHigher()) {
            String getRepliedTo = pushNotificationMessage.getRoomName();
            RemoteInput remoteInput = new RemoteInput.Builder(KEY_NOTIFICATION_REPLY)
                    .setLabel(QiscusTextUtil.getString(R.string.qiscus_reply_to, getRepliedTo.toUpperCase()))
                    .build();

            NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(android.R.drawable.ic_menu_send,
                    QiscusTextUtil.getString(R.string.qiscus_reply_to, getRepliedTo.toUpperCase()), pendingIntent)
                    .addRemoteInput(remoteInput)
                    .build();
            notificationBuilder.addAction(replyAction);
        }

        boolean cancel = false;
        if (Qiscus.getChatConfig().getNotificationBuilderInterceptor() != null) {
            cancel = !Qiscus.getChatConfig().getNotificationBuilderInterceptor()
                    .intercept(notificationBuilder, comment);
        }

        if (cancel) {
            return;
        }

        NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
        List<QiscusPushNotificationMessage> notifItems = QiscusCacheManager.getInstance()
                .getMessageNotifItems(comment.getRoomId());
        if (notifItems == null) {
            notifItems = new ArrayList<>();
        }
        int notifSize = 5;
        if (notifItems.size() < notifSize) {
            notifSize = notifItems.size();
        }
        if (notifItems.size() > notifSize) {
            inboxStyle.addLine(".......");
        }
        int start = notifItems.size() - notifSize;
        for (int i = start; i < notifItems.size(); i++) {
            inboxStyle.addLine(notifItems.get(i).getMessage());
        }
        inboxStyle.setSummaryText(QiscusTextUtil.getString(R.string.qiscus_notif_count, notifItems.size()));
        notificationBuilder.setStyle(inboxStyle);

        if (notifSize <= 3) {
            notificationBuilder.setPriority(Notification.PRIORITY_HIGH);
        }

        QiscusAndroidUtil.runOnUIThread(() -> NotificationManagerCompat.from(context)
                .notify(QiscusNumberUtil.convertToInt(comment.getRoomId()), notificationBuilder.build()));
    }

    private static void handleDeletedComment(Context context, List<QiscusComment> comments, boolean hardDelete) {
        if (comments == null || comments.isEmpty()) {
            return;
        }

        QiscusComment qiscusComment = comments.get(comments.size() - 1);

        if (hardDelete) {
            boolean removeItem = false;
            for (QiscusComment comment : comments) {
                if (QiscusCacheManager.getInstance()
                        .removeMessageNotifItem(new QiscusPushNotificationMessage(comment), comment.getRoomId())) {
                    removeItem = true;
                }
            }

            if (removeItem) {
                updateNotification(context, qiscusComment);
            }
        } else {
            boolean updateItem = false;
            for (QiscusComment comment : comments) {
                if (QiscusCacheManager.getInstance()
                        .updateMessageNotifItem(new QiscusPushNotificationMessage(comment), comment.getRoomId())) {
                    updateItem = true;
                }
            }

            if (updateItem) {
                updateNotification(context, qiscusComment);
            }
        }
    }

    private static void updateNotification(Context context, QiscusComment qiscusComment) {
        if (Qiscus.getChatConfig().isEnablePushNotification()
                && !qiscusComment.getSenderEmail().equalsIgnoreCase(Qiscus.getQiscusAccount().getEmail())) {
            if (Qiscus.getChatConfig().isOnlyEnablePushNotificationOutsideChatRoom()) {
                Pair<Boolean, Long> lastChatActivity = QiscusCacheManager.getInstance().getLastChatActivity();
                if (!lastChatActivity.first || lastChatActivity.second != qiscusComment.getRoomId()) {
                    updatePushNotification(context, qiscusComment);
                }
            } else {
                updatePushNotification(context, qiscusComment);
            }
        }
    }

    private static void updatePushNotification(Context context, QiscusComment qiscusComment) {
        List<QiscusPushNotificationMessage> items = QiscusCacheManager.getInstance()
                .getMessageNotifItems(qiscusComment.getRoomId());

        if (items == null || items.isEmpty()) {
            QiscusAndroidUtil.runOnUIThread(() -> clearPushNotification(context, qiscusComment.getRoomId()));
            return;
        }

        QiscusPushNotificationMessage lastMessage = items.get(items.size() - 1);
        if (Qiscus.getChatConfig().isEnableAvatarAsNotificationIcon()) {
            QiscusAndroidUtil.runOnUIThread(() -> loadAvatar(context, qiscusComment, lastMessage));
        } else {
            pushNotification(context, qiscusComment, lastMessage,
                    BitmapFactory.decodeResource(context.getResources(), Qiscus.getChatConfig().getNotificationBigIcon()));
        }
    }

    public static void clearPushNotification(Context context, long roomId) {
        NotificationManagerCompat.from(context).cancel(QiscusNumberUtil.convertToInt(roomId));
        QiscusCacheManager.getInstance().clearMessageNotifItems(roomId);
    }
}