package de.beyondsoft.ownchat.data.firebase;

import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;

import de.beyondsoft.ownchat.BewoplanerApplication;
import de.beyondsoft.ownchat.R;
import de.beyondsoft.ownchat.data.AppPrefsConstants;
import de.beyondsoft.ownchat.data.api.common.ErrorHandlingResponseConverter;
import de.beyondsoft.ownchat.data.api.services.ChatService;
import de.beyondsoft.ownchat.data.prefs.LongPreference;
import de.beyondsoft.ownchat.data.repository.GroupRealmRepository;
import de.beyondsoft.ownchat.data.repository.MessageRealmRepository;
import de.beyondsoft.ownchat.data.repository.specifications.GroupByIdSpecification;
import de.beyondsoft.ownchat.data.repository.specifications.MessageByIdSpecification;
import de.beyondsoft.ownchat.di.InjectionHelper;
import de.beyondsoft.ownchat.model.Group;
import de.beyondsoft.ownchat.model.Message;
import de.beyondsoft.ownchat.page.main.MainActivity;
import de.beyondsoft.ownchat.utils.Constants;
import de.beyondsoft.ownchat.utils.rx.RxUtils;
import rx.Observable;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.RemoteInput;
import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
import android.util.Log;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Named;

import static android.graphics.BitmapFactory.decodeStream;
import static de.beyondsoft.ownchat.utils.Constants.CHAT_IS_ALREADY_OPEN;

public class HandleMessagesService extends FirebaseMessagingService {

    @Inject
    ChatService mChatService;

    @Inject
    MessageRealmRepository mMessageRealmRepository;

    @Inject
    GroupRealmRepository mGroupRealmRepository;

    @Inject
    @Named(AppPrefsConstants.USER_ID)
    LongPreference mUserIdReference;

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        Log.i("FIREBASE_RECEIVER", "onMessageReceived called");

        InjectionHelper.getMessagingComponent(this).inject(HandleMessagesService.this);

        customizeNotification(remoteMessage.getData());
    }

    private void customizeNotification(Map<String, String> dataMap) {
        String messageId = dataMap.get(Constants.MESSAGE_ID);

        mChatService.getMessage(messageId)
                .compose(RxUtils.provideDefaultTransformer())
                .flatMap(new ErrorHandlingResponseConverter<>())
                .flatMap(messageResponse -> Observable.just(messageResponse.response.message))
                .subscribe(this::getMessageSuccess, this::getMessageError);
    }

    @SuppressLint("StaticFieldLeak")
    private void getMessageSuccess(Message message) {
        Log.i("FIREBASE_RECEIVER", "Message received");

        Bundle bundle = new Bundle();

        bundle.putString(Constants.MESSAGE_ID, message.id);
        bundle.putLong(Constants.GROUP_ID, message.groupId);
        bundle.putLong(Constants.CREATED_AT, message.createdAt);
        bundle.putString(Constants.MESSAGE_TEXT, message.message);
        bundle.putString(Constants.FILE, message.filePath);
        bundle.putString(Constants.SMALLER_IMAGE, message.smallerImage);
        bundle.putString(Constants.FILENAME, message.originalFilename);
        bundle.putLong(Constants.USER_ID, message.senderId);
        bundle.putString(Constants.USERNAME, message.senderName);
        bundle.putString(Constants.USER_AVATAR, message.senderAvatar);

        try {
            new DownloadAvatarTask() {
                @Override
                protected void onPostExecute(Bitmap bitmap) {
                    Intent intent = new Intent();
                    intent.putExtra(Constants.MESSAGE, bundle);
                    intent.setAction(Constants.NOTIFICATION_BROADCAST);

                    sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
                        @Override
                        public void onReceive(Context context, Intent intent) {
                            Bundle results = getResultExtras(true);

                            if (!results.getBoolean(CHAT_IS_ALREADY_OPEN, false)) {
                                int groupId = (int) bundle.getLong(Constants.GROUP_ID);
                                int numberOfUsers = message.numberOfGroupUsers;
                                intent = MainActivity.createIntent(context, bundle, numberOfUsers, groupId);

                                PendingIntent pendingIntent = PendingIntent.getActivity(context, groupId, intent, PendingIntent.FLAG_UPDATE_CURRENT);

                                Bitmap bitmapToDisplay = bitmap;
                                if (bitmapToDisplay == null) {
                                    bitmapToDisplay = BitmapFactory.decodeResource(context.getResources(), R.drawable.avatar);
                                }

                                if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                                    NotificationUtils notificationUtils = BewoplanerApplication.getNotificationUtils();

                                    if(notificationUtils.getManager() != null) {
                                        Notification.Builder nb = new Notification.Builder(context, context.getString(R.string.default_notification_channel_id))
                                                .setSmallIcon(getNotificationIcon())
                                                .setContentTitle(setNotificationTitle(message, message.numberOfGroupUsers))
                                                .setContentText(getTextFromNotification(message))
                                                .setAutoCancel(false)
                                                .setStyle(new Notification.BigTextStyle().bigText(getTextFromNotification(message)))
                                                .setContentIntent(pendingIntent)
                                                .setLargeIcon(getCircleBitmap(bitmapToDisplay))
                                                .setGroup(String.valueOf(groupId))
                                                .setGroupSummary(true)
                                                .setColor(ContextCompat.getColor(context, R.color.colorDefaultBright));

                                        Notification.Action action2 = createAction2(context, numberOfUsers, groupId, bundle);

                                        if(action2 != null) {
                                            nb.addAction(action2);
                                        }

                                        notificationUtils.getManager().notify(groupId, nb.build());
                                    }
                                } else {
                                    NotificationCompat.Action action = createAction(context, numberOfUsers, groupId, bundle);

                                    NotificationCompat.Builder builder = new NotificationCompat.Builder(context, getString(R.string.default_notification_channel_id))
                                            .setSmallIcon(getNotificationIcon())
                                            .setContentTitle(setNotificationTitle(message, message.numberOfGroupUsers))
                                            .setContentText(getTextFromNotification(message))
                                            .setAutoCancel(false)
                                            .setStyle(new NotificationCompat.BigTextStyle().bigText(getTextFromNotification(message)))
                                            .setPriority(Notification.PRIORITY_MAX)
                                            .setContentIntent(pendingIntent)
                                            .setLargeIcon(getCircleBitmap(bitmapToDisplay))
                                            .setGroup(String.valueOf(groupId))
                                            .setGroupSummary(true)
                                            .setDefaults(Notification.DEFAULT_ALL)
                                            .setColor(ContextCompat.getColor(context, R.color.colorDefaultBright));

                                    if (action != null) {
                                        builder.addAction(action);
                                    }

                                    NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

                                    if(notificationManager != null) {
                                        notificationManager.notify(groupId, builder.build());
                                    }
                                }

                                Message message = new Message(bundle.getString(Constants.MESSAGE_ID),
                                        groupId,
                                        bundle.getLong(Constants.CREATED_AT),
                                        bundle.getString(Constants.MESSAGE_TEXT),
                                        bundle.getString(Constants.FILE),
                                        bundle.getString(Constants.SMALLER_IMAGE),
                                        bundle.getString(Constants.FILENAME),
                                        null,
                                        bundle.getLong(Constants.USER_ID),
                                        bundle.getString(Constants.USERNAME),
                                        bundle.getString(Constants.USER_AVATAR),
                                        Message.Status.DELIVERED,
                                        false,
                                        mUserIdReference.get(),
                                        false);

                                storeMessageLocally(message);
                                storeGroupLocally(groupId, message);
                            }
                        }
                    }, null, Activity.RESULT_OK, null, null);
                }
            }.execute(new URL(message.senderAvatar));
        } catch(MalformedURLException e) {
            e.printStackTrace();
        }
    }

    private void getMessageError(Throwable throwable) {
        throwable.printStackTrace();
    }

    private Bitmap getCircleBitmap(Bitmap bitmap) {
        final Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        final Canvas canvas = new Canvas(output);

        final int color = Color.RED;
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
        final RectF rectF = new RectF(rect);

        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        canvas.drawOval(rectF, paint);

        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint);

        bitmap.recycle();

        return output;
    }

    private void storeGroupLocally(long groupId, Message message) {
        Group group = new Group();
        group.id = groupId;
        group.lastMessage = message;
        mGroupRealmRepository.updateGroupMessage(group, new GroupByIdSpecification(mUserIdReference.get())).subscribe();
    }

    private void storeMessageLocally(Message message) {
        MessageByIdSpecification messageByIdSpecification = new MessageByIdSpecification(mUserIdReference.get());
        messageByIdSpecification.setMessageId(message.id);
        mMessageRealmRepository.update(Collections.singletonList(message), messageByIdSpecification).subscribe();
    }

    private String setNotificationTitle(Message message, int numberOfUsers) {
        if (numberOfUsers > 2) {
            return message.senderName + " @ " + message.groupName;
        } else {
            return message.senderName;
        }
    }

    private NotificationCompat.Action createAction(Context context, int noOfUsers, int groupId, Bundle bundle) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            return null;
        }

        Intent replyIntent = ReplyNotificationReceiver.createReplyIntent(context, bundle, noOfUsers, groupId);

        PendingIntent replyPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), groupId, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        String replyLabel = getResources().getString(R.string.chat_reply_label);
        RemoteInput remoteInput = new RemoteInput.Builder(Constants.KEY_TEXT_REPLY)
                .setLabel(replyLabel)
                .build();

        return new NotificationCompat.Action.Builder(R.drawable.send_enabled, getString(R.string.chat_reply_label), replyPendingIntent)
                .addRemoteInput(remoteInput)
                .build();
    }

    private Notification.Action createAction2(Context context, int noOfUsers, int groupId, Bundle bundle) {
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            return null;
        }

        Intent replyIntent = ReplyNotificationReceiver.createReplyIntent(context, bundle, noOfUsers, groupId);

        PendingIntent replyPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), groupId, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        String replyLabel = getResources().getString(R.string.chat_reply_label);

        android.app.RemoteInput remoteInput = new android.app.RemoteInput.Builder(Constants.KEY_TEXT_REPLY)
                .setLabel(replyLabel)
                .build();

        return new Notification.Action.Builder(R.drawable.send_enabled, getString(R.string.chat_reply_label), replyPendingIntent)
                .addRemoteInput(remoteInput)
                .build();
    }

    private String getTextFromNotification(Message message) {
        String text = null;

        if(message.message != null) {
            text = message.message;
        }

        if(!TextUtils.isEmpty(message.originalFilename)) {
            text = getString(R.string.chat_file_received);
        }

        return text;
    }

    public static int getNotificationIcon() {
        boolean useWhiteIcon = (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP);
        return useWhiteIcon ? R.drawable.icon_notification_transparent : R.drawable.notification_color;
    }

    public static CharSequence getReplyMessage(Intent intent) {
        Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);

        if (remoteInput != null) {
            return remoteInput.getCharSequence(Constants.KEY_TEXT_REPLY);
        }

        return null;
    }

    private static class DownloadAvatarTask extends AsyncTask<URL, Void, Bitmap> {

        @Override
        protected Bitmap doInBackground(URL... urls) {
            if(urls == null || urls.length == 0) {
                return null;
            }

            HttpURLConnection connection;

            try {
                connection = (HttpURLConnection) urls[0].openConnection();

                connection.setDoInput(true);
                connection.connect();

                Bitmap bitmap = decodeStream(connection.getInputStream());

                final double minSize = 240d;
                int outWidth, outHeight, x, y;
                double inWidth = (double) bitmap.getWidth();
                double inHeight = (double) bitmap.getHeight();

                if(inWidth > inHeight) {
                    outHeight = (int) minSize;
                    outWidth = (int) ((minSize / inHeight) * inWidth);
                    x = (int) ((outWidth - minSize) / 2);
                    y = 0;
                } else {
                    outWidth = (int) minSize;
                    outHeight = (int) ((minSize / inWidth) * inHeight);
                    x = 0;
                    y = (int) ((outHeight - minSize) / 2);
                }

                if(x < 0) {
                    x = 0;
                }

                if(y < 0) {
                    y = 0;
                }

                return Bitmap.createBitmap(Bitmap.createScaledBitmap(bitmap, outWidth, outHeight, false), x, y, 240, 240);
            } catch(IOException e) {
                e.printStackTrace();
                return null;
            }
        }
    }
}
