namespace Guru
{
    using System;
    using System.Linq;
    using UnityEngine;
    using UnityEngine.Purchasing;
    using UnityEngine.Purchasing.Security;
    using System.Collections.Generic;
    using Firebase.Crashlytics;
    using Guru.LitJson;
    
    public abstract class IAPServiceBase<T>: IStoreListener where T: IAPServiceBase<T> , new()
    {

        #region 属性定义

        private const string Tag = "[IAP]";
        private const string DefaultCategory = "Store";

        private static bool _showLog;
        
        private ConfigurationBuilder _configBuilder; // 商店配置创建器
        
        private IStoreController _storeController;
        private IExtensionProvider _storeExtensionProvider;
        private IAppleExtensions _appleExtensions;
        private IGooglePlayStoreExtensions _googlePlayStoreExtensions;
        
        private CrossPlatformValidator _validator;
        private Dictionary<string, ProductInfo> _products;
        protected Dictionary<string, ProductInfo> Products => _products;

        public bool IsInitialized => _storeController != null && _storeExtensionProvider != null;
        
        /// <summary>
        /// 是否是首次购买
        /// </summary>
        public int PurchaseCount
        {
            get => PlayerPrefs.GetInt(nameof(PurchaseCount), 0);
            set => PlayerPrefs.SetInt(nameof(PurchaseCount), value);
        }
        
        /// <summary>
        /// 是否是首个IAP
        /// </summary>
        public bool IsFirstIAP => PurchaseCount == 0;

        private byte[] _googlePublicKey;
        private byte[] _appleRootCert;
        
        /// <summary>
        /// 服务初始化回调
        /// </summary>
        public event Action<bool> OnInitResult;
        
        /// <summary>
        /// 恢复购买回调
        /// </summary>
        public event Action<bool, string> OnRestored;

        public event Action<string> OnBuyStart;
        public event Action<string, bool> OnBuyEnd;
        public event Action<string, string> OnBuyFailed;
        public event Action<string, string, bool> OnGetProductReceipt;

#if UNITY_IOS
        /// <summary>
        /// AppStore 支付, 处理苹果支付延迟反应
        /// </summary>
        /// <returns></returns>
        public Action<Product> OnAppStorePurchaseDeferred;
#endif

        #endregion
        
        #region 单利模式
        
        protected static T _instance;
        private static object _locker = new object();
        
        public static T Instance
        {
            get
            {
                if (null == _instance)
                {
                    lock (_locker)
                    {
                        _instance = Activator.CreateInstance<T>();
                        _instance.OnCreatedInit();
                    }
                }
                return _instance;
            }
        }

        /// <summary>
        /// 组件创建初始化
        /// </summary>
        protected virtual void OnCreatedInit()
        {
            Debug.Log("--- IAPService Init");
        }
        

        #endregion
        
        #region 初始化

        /// <summary>
        /// 初始化支付服务
        /// </summary>
        public virtual void Initialize(bool showLog = false)
        {
            _showLog = showLog;
            InitPurchasing();
        }
        
        /// <summary>
        /// 带有校验器的初始化
        /// </summary>
        /// <param name="googlePublicKey"></param>
        /// <param name="appleRootCert"></param>
        /// <param name="showLog"></param>
        public virtual void InitWithKeys(byte[] googlePublicKey, byte[] appleRootCert, bool showLog = false)
        {
            _googlePublicKey = googlePublicKey;
            _appleRootCert = appleRootCert;
            Initialize(showLog);
        }

        /// <summary>
        /// 初始化支付插件
        /// </summary>
        protected virtual void InitPurchasing()
        {
            _configBuilder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
            // 注入初始商品产品列表
            var settings = GetProductSettings();
            if (null != settings)
            {
                int len = settings.Length;
                
                if(_products != null) _products.Clear();
                _products = new Dictionary<string, ProductInfo>(len);
                
                ProductSetting item;
                IDs ids;
                bool emptyIDs = false;
                for (int i = 0; i < len; i++)
                {
                    item = settings[i];
                    ids = new IDs();
                    if (!string.IsNullOrEmpty(item.GooglePlayProductId))
                    {
                        ids.Add(item.GooglePlayProductId, GooglePlay.Name);
                    }
                    else
                    {
#if UNITY_ADNROID
                        emptyIDs = true;
                        LogE($"[IAP] --- GoogleProductId is empty, please check the product setting: {item.ProductName}");
#endif
                    }


                    if (!string.IsNullOrEmpty(item.AppStoreProductId))
                    {
                        ids.Add(item.AppStoreProductId, AppleAppStore.Name);
                    }
                    else
                    {
#if UNITY_IOS
                       emptyIDs = true;
                        LogE($"[IAP] --- AppleProductId is empty, please check the product setting: {item.ProductName}");
#endif
                    }

                    if (emptyIDs)
                    {
                        continue;
                    }
                    
                    _configBuilder.AddProduct(item.ProductId, item.Type, ids); // 添加商品

                    // 建立本地的商品信息列表
                    if (string.IsNullOrEmpty(item.Category)) item.Category = DefaultCategory;
                    _products[item.ProductId] = new ProductInfo() { Setting = item };
                }
            }
            // 调用插件初始化
            UnityPurchasing.Initialize(this, _configBuilder);
        }
        
         /// <summary>
        /// 初始化成功
        /// </summary>
        /// <param name="controller"></param>
        /// <param name="extensions"></param>
        /// <exception cref="NotImplementedException"></exception>
        public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
        {
            LogI($"--- IAP Initialized Success");
            _storeController = controller;
            _storeExtensionProvider = extensions;
            
#if UNITY_IOS
            _appleExtensions = extensions.GetExtension<IAppleExtensions>();
            // On Apple platforms we need to handle deferred purchases caused by Apple's Ask to Buy feature.
            // On non-Apple platforms this will have no effect; OnDeferred will never be called.
            _appleExtensions.RegisterPurchaseDeferredListener(item =>
            {
                LogI("Purchase deferred: " + item.definition.id);
                OnAppStorePurchaseDeferred?.Invoke(item);
            });
#elif UNITY_ANDROID
            _configBuilder.Configure<IGooglePlayConfiguration>().SetObfuscatedAccountId(IPMConfig.IPM_UID);
            _googlePlayStoreExtensions = extensions.GetExtension<IGooglePlayStoreExtensions>();
            // _googlePlayStoreExtensions.SetObfuscatedAccountId(IPMConfig.IPM_UID);
            //添加安装游戏后第一次初试化进行恢复购买的回调 只有安卓才有
            _googlePlayStoreExtensions.RestoreTransactions(OnRestoreHandle);
#endif
            
            foreach (var item in _storeController.products.all)
            {
                if (!item.availableToPurchase)
                {
                    continue;
                }
                
                if (_products.ContainsKey(item.definition.id))
                {
                    _products[item.definition.id].SetProduct(item);
                }
            }

            InitValidator(); // 初始化订单验证器
            OnInitResult?.Invoke(true);
        }

         /// <summary>
         /// 初始化失败
         /// </summary>
         /// <param name="error"></param>
         /// <exception cref="NotImplementedException"></exception>
        public void OnInitializeFailed(InitializationFailureReason error)
        {
            LogE($"--- IAP Initialized Fail: {error}");
            OnInitResult?.Invoke(false);
        }

         /// <summary>
         /// 初始化失败
         /// </summary>
         /// <param name="error"></param>
         /// <param name="message"></param>
         /// <exception cref="NotImplementedException"></exception>
         public void OnInitializeFailed(InitializationFailureReason error, string message)
        {
            LogE($"--- IAP Initialized Fail: {error}   msg: {message}");
            OnInitResult?.Invoke(false);
        }

        #endregion

        #region 数据查询
        
        // <summary>
        /// 获取商品Info
        /// </summary>
        /// <param name="productName">商品名称</param>
        /// <returns></returns>
        public ProductInfo GetInfo(string productName)
        {
            if(null == Products || Products.Count == 0 ) return null;
            return Products.Values.FirstOrDefault(c => c.Name == productName);
        }
        
        /// <summary>
        /// 通过商品ID获取对应的信息
        /// </summary>
        /// <param name="productId">商品ID</param>
        /// <returns></returns>
        public ProductInfo GetInfoById(string productId)
        {
            if(null == Products || Products.Count == 0 ) return null;
            return Products.Values.FirstOrDefault(c => c.Id == productId);
        }

        /// <summary>
        /// 获取道具价格
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public double GetProductPrice(string name)
        {
            if (_storeController == null || _storeController.products == null)
            {
                return Fallback();
            }

            ProductInfo info = GetInfo(name);
            var product = _storeController.products.WithID(info.Id);
            if (product == null)
                return Fallback();

            return (double)product.metadata.localizedPrice;

            double Fallback()
            {
                ProductInfo info = GetInfo(name);
                return info?.Price ?? 0.0;
            }
        }
        
        
        /// <summary>
        /// 获取道具价格（带单位 $0.01）
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public string GetProductPriceString(string name)
        {
            if (_storeController == null || _storeController.products == null)
            {
                return Fallback();
            }

            ProductInfo info = GetInfo(name);
            var product = _storeController.products.WithID(info.Id);
            if (product == null)
                return Fallback();

            return product.metadata.localizedPriceString;

            string Fallback()
            {
                ProductInfo info = GetInfo(name);
                var pr = info?.Price ?? 0.0;
                return "$" + pr;
            }
        }

        /// <summary>
        /// 获取 IAP 内置商品
        /// </summary>
        /// <param name="productName"></param>
        /// <returns></returns>
        public Product GetProduct(string productName)
        {
            if (_storeController != null && _storeController.products != null)
            {
                var info = GetInfo(productName);
                if (info != null)
                {
                    return _storeController.products.WithID(info.Id);
                }
                Debug.LogError($"[IAP] --- can't find <ProductInfo> with name {productName}");
            }
            
            // 商品不存在则返回 NULL
            Debug.LogError($"[IAP] --- _storeController is null or products is null or products.all.Length == 0");
            return null;
        }

        /// <summary>
        /// 当前的商品是否已经持有收据了
        /// </summary>
        /// <param name="productName"></param>
        /// <returns></returns>
        public bool IsProductHasReceipt(string productName)
        {
            var product = GetProduct(productName);
            if (product != null) return product.hasReceipt;
            return false;
        }


        #endregion

        #region 订单验证器

        /// <summary>
        /// 是否支持订单校验
        /// </summary>
        /// <returns></returns>
        private bool IsCurrentStoreSupportedByValidator() 
            => IsGooglePlayStoreSelected() || IsAppleAppStoreSelected();


        /// <summary>
        /// Google 商店支持
        /// </summary>
        /// <returns></returns>
        private bool IsGooglePlayStoreSelected()
        {
            var currentAppStore = StandardPurchasingModule.Instance().appStore;
            return currentAppStore == AppStore.GooglePlay;
        }

        /// <summary>
        /// Apple 商店支持
        /// </summary>
        /// <returns></returns>
        private bool IsAppleAppStoreSelected()
        {
            var currentAppStore = StandardPurchasingModule.Instance().appStore;
            return currentAppStore == AppStore.AppleAppStore || currentAppStore == AppStore.MacAppStore;
        }

        /// <summary>
        /// 初始化订单校验器
        /// </summary>
        protected virtual void InitValidator()
        {
            if (IsCurrentStoreSupportedByValidator())
            {
                try
                {
                    if (_googlePublicKey != null && _appleRootCert != null)
                    {
                        _validator = new CrossPlatformValidator(_googlePublicKey, _appleRootCert, Application.identifier);
                    }
                    else
                    {
                        Crashlytics.LogException(new Exception($"[IAP] Init Validator failed -> googlePublicKey: {_googlePublicKey}  appleRootCert: {_appleRootCert}"));
                    }
                }
                catch (NotImplementedException exception)
                {
                    LogE("Cross Platform Validator Not Implemented: " + exception);
                }
            }
        }


        #endregion

        #region 恢复购买

        /// <summary>
        /// 恢复购买
        /// </summary>
        /// <param name="success"></param>
        /// <param name="msg"></param>
        protected virtual void OnRestoreHandle(bool success, string msg)
        {
            LogI($"--- Restore complete: {success}: msg:{msg}" );

            
            if (success)
            {
                bool isIAPUser = false;
                // 扫描所有商品, 追加用户属性
                for (int i = 0; i < _storeController.products.all.Length; i++)
                {
                    var product = _storeController.products.all[i];
                    if (product.hasReceipt)
                    {
                        isIAPUser = true;
                    }
                }
                SetIsIAPUser(isIAPUser);
            }
            
            OnRestored?.Invoke(success, msg);
        }

        /// <summary>
        /// 恢复购买道具
        /// </summary>
        public virtual void Restore()
        {
            if (!IsInitialized) return;

#if UNITY_IOS
            _appleExtensions.RestoreTransactions(OnRestoreHandle);
#elif UNITY_ANDROID
            _googlePlayStoreExtensions.RestoreTransactions(OnRestoreHandle);
#endif
        }
        

        #endregion

        #region 购买流程
        
        /// <summary>
        /// 购买商品
        /// </summary>
        /// <param name="productName"></param>
        public virtual T Buy(string productName)
        {
            if (!IsInitialized)
            {
                LogE("Buy FAIL. Not initialized.");
                OnBuyEnd?.Invoke(productName, false);
                return (T)this;
            }

            ProductInfo info = GetInfo(productName);
            if (info == null)
            {
                LogE($"Buy FAIL. No product with name: {productName}");
                OnBuyEnd?.Invoke(productName, false);
                return (T)this;
            }
            
            Product product = _storeController.products.WithID(info.Setting.ProductId);
            if (product != null && product.availableToPurchase)
            {
#if UNITY_ANDROID
                _configBuilder
                    .Configure<IGooglePlayConfiguration>()
                    .SetObfuscatedAccountId(IPMConfig.IPM_UID);
#endif
                _storeController.InitiatePurchase(product);

                Analytics.IAPClick(info?.Category??"none", product.definition.id);
                Analytics.IAPImp(DefaultCategory, product.definition.id);

                OnBuyStart?.Invoke(productName);
                return (T)this;
            }

            // 找不到商品
            LogE($"Can't find product by name: {productName}, pay canceled.");
            OnPurchaseOver(false, productName);
            OnBuyEnd?.Invoke(productName, false);

            return (T)this;
        }
        
        /// <summary>
        /// 处理支付流程
        /// </summary>
        /// <param name="purchaseEvent"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs purchaseEvent)
        {
            string productId = purchaseEvent.purchasedProduct.definition.id;
            ProductInfo info = GetInfoById(productId);
            bool success = false;
            
            if (null != info)
            {
                if (IsFirstIAP) Analytics.FirstIAP(info.Id, info.Price, info.CurrencyCode); // 上报首次支付打点
                Analytics.ProductIAP(info.Id,info.Id, info.Price, info.CurrencyCode);
                Analytics.IAPRetTrue(info.Category, info.Id, info.Price, info.CurrencyCode, info.Type, info.IsFree);
                success = true;
                
                SetIsIAPUser(success); // 设置用户属性标记
            }
            
            PurchaseCount++; // 记录支付次数
            Debug.Log($"############ ProcessPurchase: PurchaseCount:{PurchaseCount}");
            ReportPurchaseResult(purchaseEvent); // 支付结果上报

            var pp = purchaseEvent.purchasedProduct;
            if ( pp == null || string.IsNullOrEmpty(pp.receipt))
            {
                string msg = $"{Tag} ---  Purchased product is null or has no receipt!!";
                Debug.LogError(msg);
                Crashlytics.LogException(new Exception(msg));
            }
            else
            {
                OnGetProductReceipt?.Invoke(pp.definition.id, pp.receipt, pp.appleProductIsRestored);
            }
            
            string productName = info?.Name ?? "NULL";
            LogI($"{Tag} --- OnPurchaseSuccess :: purchase count: {PurchaseCount}  productName: {productName}");
            
            OnPurchaseOver(success, productName); // 支付成功处理逻辑
            OnBuyEnd?.Invoke(productName, success);
            
            return PurchaseProcessingResult.Complete; // 直接Consume 掉当前的商品
        }

        /// <summary>
        /// 支付失败
        /// </summary>
        /// <param name="product"></param>
        /// <param name="failureReason"></param>
        /// <exception cref="NotImplementedException"></exception>
        public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
        {
            string productId = product.definition.id;
            ProductInfo info = GetInfoById(productId);
        
            //上报点位，用户购买失败的原因
            if (failureReason == PurchaseFailureReason.UserCancelled)
            {
                Analytics.IAPClose(info.Category, product.definition.id);
            }
            else
            {
                Analytics.IAPRetFalse(info.Category, product.definition.id, failureReason.ToString());
            }

            LogI($"{Tag} --- OnPurchaseFailed :: failureReason = {failureReason}");
            // 失败的处理逻辑
            OnPurchaseOver(false, info.Name);
            OnBuyEnd?.Invoke(info.Name, false);
            // 失败原因
            OnBuyFailed?.Invoke(info.Name, failureReason.ToString());
        }


        /// <summary>
        /// 获取商品的本地化价格字符串
        /// 如果商品不存在或者 IAP 尚未初始化完成则显示 "Loading" 字样
        /// </summary>
        /// <param name="productName"></param>
        /// <returns></returns>
        public string GetLocalizedPriceString(string productName)
        {
            return GetInfo(productName)?.LocalizedPriceString ?? "Loading";
        }

        #endregion

        #region Log 输出

        /// <summary>
        /// 日志输出
        /// </summary>
        /// <param name="msg"></param>
        private static void LogI(object msg)
        {
            if (_showLog) 
                Debug.Log($"{Tag} {msg}");
        }

        private static void LogE(object msg)
        {
            if (_showLog) 
                Debug.LogError($"{Tag} {msg}");
        }

        private static void LogW(object msg)
        {
            if (_showLog)
                Debug.LogWarning($"{Tag} {msg}");
        }

        #endregion

        #region 实现接口

        /// <summary>
        /// 需要游戏侧继承并完成Blevel的取值上报
        /// </summary>
        /// <returns></returns>
        protected abstract int GetBLevel();

        /// <summary>
        /// 获取商品品配置列表
        /// </summary>
        /// <returns></returns>
        protected virtual ProductSetting[] GetProductSettings()
            => GuruSettings.Instance.Products;

        /// <summary>
        /// 支付回调
        /// </summary>
        /// <param name="success">是否成功</param>
        /// <param name="productName">商品名称</param>
        protected abstract void OnPurchaseOver(bool success, string productName);

        #endregion
        
        #region 支付上报逻辑

        /// <summary>
        /// 支付结果上报
        /// </summary>
        protected virtual void ReportPurchaseResult(PurchaseEventArgs args)
        {
            int blevel = GetBLevel();
            int orderType = args.purchasedProduct.definition.type == ProductType.Subscription ? 1 : 0;


            Debug.Log($"############ ReportPurchaseResult: _validator:{_validator }");
            
            if (_validator == null)
            {
                Debug.Log($"############ --- Validator is null");
                LogE($"{Tag} --- Validator is null. Report Order failed.");
                Crashlytics.LogException(new Exception($"IAPService can not report order because Validator is null!"));
                return;
            }
            
            try
            {
                // ----- 支付后的b_level上报逻辑 
                LogI($"--- Report b_level:[{blevel}] with product id:{args.purchasedProduct.definition.id} ");
                
                
#if UNITY_EDITOR
                // Editor 不做上报逻辑
#elif UNITY_ANDROID
                // Android 订单验证, 上报打点信息
                var result = _validator.Validate(args.purchasedProduct.receipt);
                string productID = orderType == 0 ? args.purchasedProduct.definition.id : "";
                string subscriptionID = orderType == 1 ? args.purchasedProduct.definition.id : "";
                
                LogI($"{Tag} --- Report Android IAP Order -> orderType:{orderType}  productID:{productID}  blevel:{blevel}");
                
                Debug.Log($"############ --- result.Count: {result.Length}");

                foreach (var productReceipt in result)
                {
                    if (productReceipt is GooglePlayReceipt google)
                    {
                        Debug.Log($"{Tag} --- Report Android IAP Order -> orderType:{orderType}  productID:{productID}  blevel:{blevel}");
                        new GoogleOrderRequest(orderType, productID, subscriptionID, google.purchaseToken, blevel).SetTimeOut(5).Send();
                    }
                }
#elif UNITY_IOS
                // iOS 订单验证, 上报打点信息
                var jsonData = JsonMapper.ToObject(args.purchasedProduct.receipt);
                string receipt = jsonData["Payload"].ToString();
                if (HasReceipt(receipt))
                {
                    Debug.Log($"[IAP] Receipt has already reported: {receipt}");
                    return;
                }
                AddReceipt(receipt);
                new AppleOrderRequest(orderType, args.purchasedProduct.definition.id, receipt,blevel).Send();
                Debug.Log($"{Tag} --- Report iOS IAP Order -> orderType:{orderType}  productID:{args.purchasedProduct.definition.id}  blevel:{blevel}");
#endif
            }
            catch (Exception e)
            {
                LogE($" [IAPManager.RevenueUpload] got Exception: {e.Message}");
                Crashlytics.LogException(new Exception($"[IAP] Unity report purchase data with b_level={blevel} got error: {e.Message}"));
            }
        }

        #endregion
        
        #region IOS Orders Collection
        
        private HashSet<string> iOSReceipts;
        public HashSet<string> IOSReceiptCollection
        {
            get
            {
                // 读取订单信息
                if (iOSReceipts == null)
                {
                    iOSReceipts = new HashSet<string>();
                    string raw = PlayerPrefs.GetString(nameof(IOSReceiptCollection), "");
                    if (!string.IsNullOrEmpty(raw))
                    {
                        var arr = raw.Split(',');
                        for (int i = 0; i < arr.Length; i++)
                        {
                            iOSReceipts.Add(arr[i]);
                        }
                    }
                }
                return iOSReceipts;
            }

            set
            {
                // 保存订单信息
                iOSReceipts = value;
                PlayerPrefs.SetString(nameof(IOSReceiptCollection), string.Join(",", iOSReceipts));
                PlayerPrefs.Save();
            }
        }

        /// <summary>
        /// 添加订单信息
        /// </summary>
        /// <param name="receipt"></param>
        public void AddReceipt(string receipt)
        {
            if (!HasReceipt(receipt))
            {
                IOSReceiptCollection.Add(receipt);
            }
        }

        /// <summary>
        /// 是否包含订单
        /// </summary>
        /// <param name="receipt"></param>
        /// <returns></returns>
        public bool HasReceipt(string receipt)
        {
            return IOSReceiptCollection.Contains(receipt);
        }


        #endregion

        #region 用户标志位设置
        
        /// <summary>
        /// 标记是否为付费用户
        /// </summary>
        /// <param name="value"></param>
        public static void SetIsIAPUser(bool value = true)
        {
            Analytics.SetUserProperty(Analytics.PropertyIsIAPUser, value ? "true" : "false");
        }


        #endregion
    }

}