using System;
using System.Collections.Generic;
using System.Linq;
using _Combo.Events;
using _Core;
using Seayoo.ComboSDK.Windows;

namespace _Combo
{
    internal class StoreManager : Singleton<StoreManager>
    {
        private Action<ComboResult<ComboSDKPaymentResult>> PurchaseFinishedAction;
        public string store;
        public Dictionary<Module, IStore> storeDic = new Dictionary<Module, IStore>();
        private Order order;

        private StoreManager() {
            EventSystem.Register(this);
        }

        public void Setup(List<string> modules, StoreParameters storeParameters)
        {
            List<Module> storeModules = new List<Module>();
            foreach (string module in modules)
            {
                if(ConvertToModule(module) != null)
                {
                    storeModules.Add((Module)ConvertToModule(module));
                }
            }

            foreach(var storeModule in storeModules)
            {
                IStore entity = Reflector.Instance.CreateModules<IStore>(storeModule);
                if (entity == null)
                {
                    Log.E($"Load {storeModule} store failed!");
                    break;
                }
                entity.Setup(storeParameters);
                storeDic[storeModule] = entity;
            }
        }

        public void Purchase(string orderToken, Action<ComboResult<ComboSDKPaymentResult>> action)
        {
            PurchaseFinishedAction = action;
            if(storeDic == null || storeDic.Values == null || !storeDic.Values.Any())
            {
                var error = new InternalError(new FeatureNotAvailableError("Purchase"));
                PurchaseFailedEvent.Invoke(new PurchaseFailedEvent{ error = error });
                return;
            }
            ConfirmOrder(orderToken, storeDic.Values.First(), result =>{ });
        }

        private void ConfirmOrder(string orderToken, IStore store, Action<Response> action)
        {
            UIController.Instance.ShowLoading();
            ComboClient.ConfirmOrder(orderToken, store.GetStore(), null, result => {
                UIController.Instance.HideLoading();
                action.Invoke(new Response{
                    order = result.Data.order,
                    store = result.Data.store,
                    purchaseOptions = result.Data.purchaseOptions,
                });
                order = result.Data.order;
                store.Purchase(orderToken, result.Data.order, result.Data.purchaseOptions);
            }, (err) => {
                UIController.Instance.HideLoading();
                PurchaseFailedEvent.Invoke(new PurchaseFailedEvent{ error = err });
            });
        }

        public bool IsAvailableProductQuantity()
        {
            if(storeDic == null || storeDic.Values == null || !storeDic.Values.Any())
            {
                return false;
            }
            return storeDic.Values.First().IsFeatureAvailable(Feature.PRODUCT_QUANTITY);
        }

        [EventSystem.BindEvent]
        public void PurchaseCancel(PurchaseCancelEvent e)
        {
            var error = new InternalError(I18n.T("user_cancelled_error"), new UserCancelled());
            PurchaseFinishedAction?.Invoke(ComboResult<ComboSDKPaymentResult>.CreateFail(error));
        }

        [EventSystem.BindEvent]
        public void PurchaseSuccess(PurchaseSuccessEvent e)
        {
            var result = new ComboSDKPaymentResult { orderId = e.orderId };
            PurchaseFinishedAction?.Invoke(ComboResult<ComboSDKPaymentResult>.CreateSuccess(result));
        }

        [EventSystem.BindEvent]
        public void PurchaseFail(PurchaseFailedEvent e)
        {
            ErrorTrackingManager.Instance.CaptureException(e.error, (callback) => {
                callback.ContextMap["Order"] = ToDictionaty(order);
            });
            PurchaseFinishedAction?.Invoke(ComboResult<ComboSDKPaymentResult>.CreateFail(e.error));
        }


        private Module? ConvertToModule(string module)
        {
            switch (module)
            {
                case "seayoo_store":
                    return Module.SeayooStore;
                case "lenovo_pc":
                    return Module.Lenovo;
                default:
                    return null;
            }
        }

        private Dictionary<string, object> ToDictionaty(Order order)
        {
            if(order == null)
            {
                return new Dictionary<string, object>();
            }
            else
            {
                return new Dictionary<string, object>()
                {
                    { "orderId", order.orderId },
                    { "productId", order.productId },
                    { "productName", order.productName },
                    { "quantity", order.quantity },
                    { "currency", order.currency },
                    { "amount", order.amount },
                    { "status", order.status }
                };
            }
        }
    }
}