package io.fullstack.firestack;

import java.util.Map;

import android.content.Context;
import android.content.IntentFilter;
import android.content.Intent;
import android.content.BroadcastReceiver;
import android.util.Log;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.bridge.ReadableType;
import com.facebook.react.bridge.WritableMap;

import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.RemoteMessage;

/**
 * Created by nori on 2016/09/12.
 */
public class FirestackCloudMessaging extends ReactContextBaseJavaModule {

    private static final String TAG = "FirestackCloudMessaging";
    private static final String EVENT_NAME_TOKEN = "FirestackRefreshToken";
    private static final String EVENT_NAME_NOTIFICATION = "FirestackReceiveNotification";
    private static final String EVENT_NAME_SEND = "FirestackUpstreamSend";

    public static final String INTENT_NAME_TOKEN = "io.fullstack.firestack.refreshToken";
    public static final String INTENT_NAME_NOTIFICATION = "io.fullstack.firestack.ReceiveNotification";
    public static final String INTENT_NAME_SEND = "io.fullstack.firestack.Upstream";

    private ReactContext mReactContext;
    private IntentFilter mRefreshTokenIntentFilter;
    private IntentFilter mReceiveNotificationIntentFilter;
    private IntentFilter mReceiveSendIntentFilter;

    public FirestackCloudMessaging(ReactApplicationContext reactContext) {
        super(reactContext);
        mReactContext = reactContext;
        mRefreshTokenIntentFilter = new IntentFilter(INTENT_NAME_TOKEN);
        mReceiveNotificationIntentFilter = new IntentFilter(INTENT_NAME_NOTIFICATION);
        mReceiveSendIntentFilter = new IntentFilter(INTENT_NAME_SEND);
        initRefreshTokenHandler();
        initMessageHandler();
        initSendHandler();
        Log.d(TAG, "New instance");
    }

    @Override
    public String getName() {
        return TAG;
    }

    @ReactMethod
    public void getToken(final Callback callback) {

        try {
            String token = FirebaseInstanceId.getInstance().getToken();
            Log.d(TAG, "Firebase token: " + token);
            callback.invoke(null, token);
        } catch (Exception e) {
            WritableMap error = Arguments.createMap();
            error.putString("message", e.getMessage());
            callback.invoke(error);
        }
    }

    /**
     *
     */
    private void initRefreshTokenHandler() {
        getReactApplicationContext().registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                WritableMap params = Arguments.createMap();
                params.putString("token", intent.getStringExtra("token"));
                ReactContext ctx = getReactApplicationContext();
                Log.d(TAG, "initRefreshTokenHandler received event " + EVENT_NAME_TOKEN);
                FirestackUtils.sendEvent(ctx, EVENT_NAME_TOKEN, params);
            }

            ;
        }, mRefreshTokenIntentFilter);
    }

    @ReactMethod
    public void subscribeToTopic(String topic, final Callback callback) {
        try {
            FirebaseMessaging.getInstance().subscribeToTopic(topic);
            callback.invoke(null,topic);
        } catch (Exception e) {
            e.printStackTrace();
            Log.d(TAG, "Firebase token: " + e);
            WritableMap error = Arguments.createMap();
            error.putString("message", e.getMessage());
            callback.invoke(error);

        }
    }

    @ReactMethod
    public void unsubscribeFromTopic(String topic, final Callback callback) {
        try {
            FirebaseMessaging.getInstance().unsubscribeFromTopic(topic);
            callback.invoke(null,topic);
        } catch (Exception e) {
            WritableMap error = Arguments.createMap();
            error.putString("message", e.getMessage());
            callback.invoke(error);
        }
    }

    private void initMessageHandler() {
        Log.d(TAG, "Firestack initMessageHandler called");
        getReactApplicationContext().registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                RemoteMessage remoteMessage = intent.getParcelableExtra("data");
                Log.d(TAG, "Firebase onReceive: " + remoteMessage);
                WritableMap params = Arguments.createMap();
                if (remoteMessage.getData().size() != 0) {
                    WritableMap dataMap = Arguments.createMap();
                    Map<String, String> data = remoteMessage.getData();
                    //Set<String> keysIterator = data.keySet();
                    for (String key : data.keySet()) {
                        dataMap.putString(key, data.get(key));
                    }
                    params.putMap("data", dataMap);
                } else {
                    params.putNull("data");
                }
                if (remoteMessage.getNotification() != null) {
                    WritableMap notificationMap = Arguments.createMap();
                    RemoteMessage.Notification notification = remoteMessage.getNotification();
                    notificationMap.putString("title", notification.getTitle());
                    notificationMap.putString("body", notification.getBody());
                    notificationMap.putString("icon", notification.getIcon());
                    notificationMap.putString("sound", notification.getSound());
                    notificationMap.putString("tag", notification.getTag());
                    params.putMap("notification", notificationMap);
                } else {
                    params.putNull("notification");
                }
                ReactContext ctx = getReactApplicationContext();
                FirestackUtils.sendEvent(ctx, EVENT_NAME_NOTIFICATION, params);
            }
        }, mReceiveNotificationIntentFilter);
    }

    @ReactMethod
    public void send(String senderId, String messageId, String messageType, ReadableMap params, final Callback callback) {
        FirebaseMessaging fm = FirebaseMessaging.getInstance();
        RemoteMessage.Builder remoteMessage = new RemoteMessage.Builder(senderId);
        remoteMessage.setMessageId(messageId);
        remoteMessage.setMessageType(messageType);
        ReadableMapKeySetIterator iterator = params.keySetIterator();
        while (iterator.hasNextKey()) {
            String key = iterator.nextKey();
            ReadableType type = params.getType(key);
            if (type == ReadableType.String) {
                remoteMessage.addData(key, params.getString(key));
                Log.d(TAG, "Firebase send: " + key);
                Log.d(TAG, "Firebase send: " + params.getString(key));
            }
        }
        try {
            fm.send(remoteMessage.build());
            WritableMap res = Arguments.createMap();
            res.putString("status", "success");
            callback.invoke(null, res);
        } catch(Exception e) {
            Log.e(TAG, "Error sending message", e);
            WritableMap error = Arguments.createMap();
            error.putString("code", e.toString());
            error.putString("message", e.toString());
            callback.invoke(error);
        }
    }

    private void initSendHandler() {
        getReactApplicationContext().registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                WritableMap params = Arguments.createMap();
                if (intent.getBooleanExtra("hasError", false)) {
                    WritableMap error = Arguments.createMap();
                    error.putInt("code", intent.getIntExtra("errCode", 0));
                    error.putString("message", intent.getStringExtra("errorMessage"));
                    params.putMap("err", error);
                } else {
                    params.putNull("err");
                }
                ReactContext ctx = getReactApplicationContext();
                FirestackUtils.sendEvent(ctx, EVENT_NAME_SEND, params);
            }
        }, mReceiveSendIntentFilter);
    }
}