﻿using System.Collections.Generic;
using UnityEngine;

namespace Yodo1.FanCraft
{
    public class FanCraftSDK : MonoBehaviour
    {
        public static readonly string TAG = "[Yodo1FanCraftSDK] ";

        public static FanCraftSDK Instance { get; private set; }

        private void Awake()
        {
            if (Instance == null)
            {
                Instance = this;
                DontDestroyOnLoad(gameObject);
            }
        }

        public string SdkObjectName
        {
            get { return gameObject.name; }
        }

        public string SdkMethodName
        {
            get { return "FanCraftSDK_CallBackResult"; }
        }

        public void FanCraftSDK_CallBackResult(string result)
        {
            CallbackEvents(result);
        }

        private static FanCraftSDKImpi _impl;

        private static FanCraftSDKImpi Impl
        {
            get
            {
                if (_impl == null)
                {
#if UNITY_EDITOR
                    _impl = new FanCraftSDKImpi();
#elif UNITY_IPHONE || UNITY_IOS
                    _impl = new FanCraftSDK_iOS();
#elif UNITY_ANDROID
                    _impl = new FanCraftSDK_Android();
#endif
                }
                return _impl;
            }
        }

        public enum NotificationType
        {
            NotificationTypeUnKnown = -1,
            NotificationTypeText = 0,
            NotificationTypeTextImage = 1,
            NotificationTypeImage = 2,
            NotificationTypePoll = 3,
            NotificationTypePollImage = 4
        }

        public enum NotificationActionType
        {
            NotificationActionTypeUnKnown = -1,
            NotificationActionTypeExternal = 0,
            NotificationActionTypeInApp = 1,
            NotificationActionTypeReward = 2,
            //NotificationActionTypePoll = 3
        }

        public enum InterstialNotificationEvent
        {
            InterstitialNotificationWillAppear = 1,
            InterstitialNotificationDidCancel = 2,
            InterstitialNotificationDidEngagen = 3,
        }

        #region delegate

        /// <summary>
        /// The delegate event of the SDK initialization
        /// </summary>
        /// <param name="userInfo">SDKUserInfo</param>
        /// <param name="error"></param>
        public delegate void InitializeDelegate(SDKInitSuccessInfo info, FanCraftError error);
        private static InitializeDelegate _initializeDelegate;

        /// <summary>
        /// The delegate event of the identify game user
        /// </summary>
        /// <param name="userInfo">FanCraftUserInfo</param>
        /// <param name="error">FanCraftError</param>
        public delegate void IdentifyGameUserDelegate(FanCraftUserInfo userInfo, FanCraftError error);
        private static IdentifyGameUserDelegate _identifyGameUserDelegate;


        public delegate void SetUserTagsDelegate(bool status, FanCraftError error);
        private static SetUserTagsDelegate _setUserTagsDelegate;

        /// <summary>
        /// The delegate event of the Interstitial Notification
        /// </summary>
        /// <param name="notificationId">The identifier of the notification</param>
        /// <param name="error">The error is the reason why failed to display the interstitial notification</param>
        /// <param name="handler"></param>
        public delegate void InterstitialNotificationDelegate(string notificationId, FanCraftError error, InterstialNotificationHandler handler);
        private static InterstitialNotificationDelegate _interstitialNotificationDelegate;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="deviceToken"></param>
        /// <param name="error"></param>
        public delegate void RegisterForRemoteNotificationsDeviceTokenDelegate(string deviceToken, FanCraftError error);
        private static RegisterForRemoteNotificationsDeviceTokenDelegate _registerForRemoteNotificationsDeviceTokenDelegate;

        #endregion

        private static void InstantiateSdkPrefab()
        {
            if (Instance == null)
            {
                var type = typeof(FanCraftSDK);
                var sdkObj = new GameObject("FanCraftSDK", type).GetComponent<FanCraftSDK>();
                if (Instance != sdkObj)
                {
                    Debug.LogError(TAG + "looks like you have the " + type.Name + " on a GameObject in your scene. Please remove the script from your scene.");
                }
            }
        }

        public static void RegisterForRemoteNotifications(RegisterForRemoteNotificationsDeviceTokenDelegate registerForRemoteNotificationsDeviceTokenDelegate)
        {
            _registerForRemoteNotificationsDeviceTokenDelegate = registerForRemoteNotificationsDeviceTokenDelegate;
            InstantiateSdkPrefab();
            Impl.RegisterForRemoteNotifications(Instance.SdkObjectName, Instance.SdkMethodName);
        }

        /// <summary>
        /// Initialize SDK
        /// </summary>
        /// <param name="appKey">Your app key</param>
        /// <param name="gameUserSettings">The user information of your game</param>
        /// <param name="privacySettings">The privacy setting for the user, such as CCPA, COPPA, GDPR, etc</param>
        /// <param name="pushNotificationSettings">The system push settings</param>
        /// <param name="configuration">SDK configuration is for further usage</param>
        /// <param name="initializeDelegate"></param>
        public static void Init(string appKey,
                                GameUserSettings gameUserSettings,
                                PrivacySettings privacySettings,
                                PushNotificationSettings pushNotificationSettings,
                                SDKConfiguration configuration,
                                InitializeDelegate initializeDelegate)
        {
            _initializeDelegate = initializeDelegate;
            InstantiateSdkPrefab();

            string gameUserSettingJsonString = JsonUtility.Serialize(gameUserSettings.ToDictionary());
            string privacySettingsJsonString = JsonUtility.Serialize(privacySettings.ToDictionary());
            string pushNotificationSettingsJsonString = JsonUtility.Serialize(pushNotificationSettings.ToDictionary());
            string configurationJsonString = JsonUtility.Serialize(configuration.ToDictionary());

            Impl.Init(appKey, gameUserSettingJsonString, privacySettingsJsonString, pushNotificationSettingsJsonString, configurationJsonString, Instance.SdkObjectName, Instance.SdkMethodName);
        }

        /// <summary>
        /// Identify/Update game user info to server end, it will be used when the game user info changed(UserID haven't changed) in playing
        /// </summary>
        /// <param name="gameUserInfo">The user info of your game/app</param>
        /// <param name="identifyGameUserDelegate"></param>
        public static void UpdateGameUser(GameUserInfo gameUserInfo, IdentifyGameUserDelegate identifyGameUserDelegate)
        {
            _identifyGameUserDelegate = identifyGameUserDelegate;
            string gameUserJsonString = JsonUtility.Serialize(gameUserInfo.ToDictionary());
            Impl.UpdateGameUser(gameUserJsonString, Instance.SdkObjectName, Instance.SdkMethodName);
        }

        /// <summary>
        /// This method allow the developers to tag a player at specific parts of their game play.
        /// </summary>
        /// <param name="tag">A player tag at specific parts of game play</param>
        /// <param name="setUserTagsDelegate"></param>
        public static void SetTag(string tag, SetUserTagsDelegate setUserTagsDelegate)
        {
            _setUserTagsDelegate = setUserTagsDelegate;
            List<string> tags = new List<string>();
            tags.Add(tag);
            Impl.SetTags(tags, Instance.SdkObjectName, Instance.SdkMethodName);
        }

        /// <summary>
        /// This method allow the developers to tag a player at specific parts of their game play.
        /// </summary>
        /// <param name="tags">Player tags at specific parts of game play</param>
        /// <param name="setUserTagsDelegate"></param>
        public static void SetTags(string[] tags, SetUserTagsDelegate setUserTagsDelegate)
        {
            _setUserTagsDelegate = setUserTagsDelegate;
            List<string> tagsList = new List<string>();
            foreach (string tag in tags)
            {
                tagsList.Add(tag);
            }

            Impl.SetTags(tagsList, Instance.SdkObjectName, Instance.SdkMethodName);
        }

        /// <summary>
        /// This method allow the developers to tag a player at specific parts of their game play.
        /// </summary>
        /// <param name="tags">Player tags at specific parts of game play</param>
        /// <param name="setUserTagsDelegate"></param>
        public static void SetTags(List<string> tags, SetUserTagsDelegate setUserTagsDelegate)
        {
            _setUserTagsDelegate = setUserTagsDelegate;
            Impl.SetTags(tags, Instance.SdkObjectName, Instance.SdkMethodName);
        }

        /// <summary>
        /// Display the interstitial notification
        /// </summary>
        /// <param name="interstitialNotificationDelegate"></param>
        public static void ShowInterstitialNotification(InterstitialNotificationDelegate interstitialNotificationDelegate)
        {
            _interstitialNotificationDelegate = interstitialNotificationDelegate;
            Impl.ShowInterstitialNotification(Instance.SdkObjectName, Instance.SdkMethodName);
        }

        /// <summary>
        /// This method allow the developers to fetch notifications, this can be a function override for the same InterstitialNotification function.
        /// </summary>
        /// <param name="tag">A player tag at specific parts of game play</param>
        /// <param name="interstitialNotificationDelegate"></param>
        public static void ShowInterstitialNotificationByLocation(string locationId, InterstitialNotificationDelegate interstitialNotificationDelegate)
        {
            _interstitialNotificationDelegate = interstitialNotificationDelegate;
            Impl.ShowInterstitialNotificationByLocation(locationId, Instance.SdkObjectName, Instance.SdkMethodName);
        }

        public static void CallbackEvents(string result)
        {
            Debug.Log(TAG + "CallbackEvents result:" + result + "\n");
            Dictionary<string, object> dict = (Dictionary<string, object>)JsonUtility.Deserialize(result);
            if (dict == null)
            {
                return;
            }

            if (!dict.ContainsKey(SDKConstant.KEY_EVENT_NAME) || !dict.ContainsKey(SDKConstant.KEY_EVENT_DATA))
            {
                return;
            }

            // Check if the event_name vaild
            string eventName = dict[SDKConstant.KEY_EVENT_NAME].ToString();
            if (string.IsNullOrEmpty(eventName))
            {
                return;
            }

            // Check if the event_data vaild
            Dictionary<string, object> dataDict = (Dictionary<string, object>)dict[SDKConstant.KEY_EVENT_DATA];
            if (dataDict == null)
            {
                return;
            }

            // Check FanCraftError
            FanCraftError error = null;
            if (dataDict.ContainsKey(SDKConstant.KEY_ERROR))
            {
                Dictionary<string, object> errorDict = (Dictionary<string, object>)dataDict[SDKConstant.KEY_ERROR];
                error = new FanCraftError(errorDict);
            }

            // Handle all events
            if (eventName.Equals(SDKConstant.METHOD_INIT))
            {
                SDKInitSuccessInfo initSuccessInfo = null;
                if (dataDict.ContainsKey(SDKConstant.KEY_INIT_SUCCESS_INFO))
                {
                    Dictionary<string, object> initSuccessInfoDict = (Dictionary<string, object>)dataDict[SDKConstant.KEY_INIT_SUCCESS_INFO];
                    initSuccessInfo = new SDKInitSuccessInfo(initSuccessInfoDict);
                }

                if (_initializeDelegate != null)
                {
                    _initializeDelegate(initSuccessInfo, error);
                }
            }
            else if (eventName.Equals(SDKConstant.METHOD_UPDATE_GAME_USER))
            {
                FanCraftUserInfo userInfo = null;
                if (dataDict.ContainsKey(SDKConstant.KEY_USER_INFO))
                {
                    Dictionary<string, object> userInfoDict = (Dictionary<string, object>)dataDict[SDKConstant.KEY_USER_INFO];
                    userInfo = new FanCraftUserInfo(userInfoDict);
                }
                if (_identifyGameUserDelegate != null)
                {
                    _identifyGameUserDelegate(userInfo, error);
                }
            }
            else if (eventName.Equals(SDKConstant.METHOD_ADD_USER_TAGS))
            {
                if (_setUserTagsDelegate != null)
                {
                    bool status = false;
                    if (dataDict.ContainsKey(SDKConstant.KEY_STATUS))
                    {
                        status = bool.Parse(dataDict[SDKConstant.KEY_STATUS].ToString());
                    }
                    _setUserTagsDelegate(status, error);
                }
            }
            else if (eventName.Equals(SDKConstant.METHOD_INTER_NOTIFICATION_EVENT))
            {
                string notificationID = "";
                if (dataDict.ContainsKey(SDKConstant.KEY_NOTIFICATION_ID))
                {
                    notificationID = dataDict[SDKConstant.KEY_NOTIFICATION_ID].ToString();
                }

                InterstialNotificationHandler handler = null;
                if (dataDict.ContainsKey(SDKConstant.KEY_NOTIFICATION_HANDLER))
                {
                    Dictionary<string, object> handlerDict = (Dictionary<string, object>)dataDict[SDKConstant.KEY_NOTIFICATION_HANDLER];
                    handler = new InterstialNotificationHandler(handlerDict);
                }

                if (_interstitialNotificationDelegate != null)
                {
                    _interstitialNotificationDelegate(notificationID, error, handler);
                }
            }
            else if (eventName.Equals(SDKConstant.METHOD_REGISTER_REMOTE_NOTIFICATIONS))
            {
                string deviceToken = "";
                if (dataDict.ContainsKey(SDKConstant.KEY_DEVICE_TOKEN))
                {
                    deviceToken = dataDict[SDKConstant.KEY_DEVICE_TOKEN].ToString();
                }
                if (_registerForRemoteNotificationsDeviceTokenDelegate != null)
                {
                    _registerForRemoteNotificationsDeviceTokenDelegate(deviceToken, error);
                }
            }
        }

        #region Mock events - will be removed when the plugin release
        public static void CallbackEventsMockInit()
        {
            Dictionary<string, object> errorDict = new Dictionary<string, object>();
            errorDict.Add("code", 1);
            errorDict.Add("message", "Failed to init");

            Dictionary<string, object> userInfoDict = new Dictionary<string, object>();
            userInfoDict.Add("uid", "123");
            userInfoDict.Add("gameUserId", "");
            userInfoDict.Add("isAnonymous", true);
            userInfoDict.Add("deviceTokenSet", false);

            Dictionary<string, object> initSuccessInfoDict = new Dictionary<string, object>();
            initSuccessInfoDict.Add(SDKConstant.KEY_USER_INFO, userInfoDict);

            Dictionary<string, object> dataDict = new Dictionary<string, object>();
            //dataDict.Add(SDKConstant.KEY_ERROR, errorDict);
            dataDict.Add(SDKConstant.KEY_INIT_SUCCESS_INFO, initSuccessInfoDict);

            Dictionary<string, object> mockDict = new Dictionary<string, object>();
            mockDict.Add(SDKConstant.KEY_EVENT_NAME, SDKConstant.METHOD_INIT);
            mockDict.Add(SDKConstant.KEY_EVENT_DATA, dataDict);

            string reslut = JsonUtility.Serialize(mockDict);

            CallbackEvents(reslut);
        }

        public static void CallbackEventsMockUpdateGameUser()
        {
            Dictionary<string, object> errorDict = new Dictionary<string, object>();
            errorDict.Add("code", 1);
            errorDict.Add("message", "Failed to update game user");

            Dictionary<string, object> userInfoDict = new Dictionary<string, object>();
            userInfoDict.Add("uid", "123");
            userInfoDict.Add("gameUserId", "");
            userInfoDict.Add("isAnonymous", true);
            userInfoDict.Add("deviceTokenSet", false);

            Dictionary<string, object> dataDict = new Dictionary<string, object>();
            //dataDict.Add(SDKConstant.KEY_ERROR, errorDict);
            dataDict.Add(SDKConstant.KEY_USER_INFO, userInfoDict);

            Dictionary<string, object> mockDict = new Dictionary<string, object>();
            mockDict.Add(SDKConstant.KEY_EVENT_NAME, SDKConstant.METHOD_UPDATE_GAME_USER);
            mockDict.Add(SDKConstant.KEY_EVENT_DATA, dataDict);

            string reslut = JsonUtility.Serialize(mockDict);

            CallbackEvents(reslut);
        }

        public static void CallbackEventsMockNotification()
        {
            Dictionary<string, object> errorDict = new Dictionary<string, object>();
            errorDict.Add("code", 1);
            errorDict.Add("message", "Failed to update game user");

            Dictionary<string, object> reward = new Dictionary<string, object>();
            reward.Add("reward_id", "test");
            reward.Add("quantity", 5);

            List<Dictionary<string, object>> rewards = new List<Dictionary<string, object>>();
            rewards.Add(reward);

            Dictionary<string, object> handlerDict = new Dictionary<string, object>();
            handlerDict.Add("notification_event", 3);
            handlerDict.Add("notification_type", 1);
            handlerDict.Add("notification_action_type", 1);
            handlerDict.Add("notification_action_url", "https://www.yodo1.com/");
            handlerDict.Add("notification_rewards", rewards);

            Dictionary<string, object> dataDict = new Dictionary<string, object>();
            dataDict.Add(SDKConstant.KEY_NOTIFICATION_ID, "notification_id");
            //dataDict.Add(SDKConstant.KEY_ERROR, errorDict);
            dataDict.Add(SDKConstant.KEY_NOTIFICATION_HANDLER, handlerDict);

            Dictionary<string, object> mockDict = new Dictionary<string, object>();
            mockDict.Add(SDKConstant.KEY_EVENT_NAME, SDKConstant.METHOD_INTER_NOTIFICATION_EVENT);
            mockDict.Add(SDKConstant.KEY_EVENT_DATA, dataDict);

            string reslut = JsonUtility.Serialize(mockDict);

            CallbackEvents(reslut);
        }

        #endregion
    }
}