using System;
using System.Collections.Generic;
using _Core;
using Sentry;

namespace _Combo
{
    public class ErrorTrackingManager : Singleton<ErrorTrackingManager>
    {
        private Dictionary<Module, IErrorTracking> errorTrackingMap = new Dictionary<Module, IErrorTracking>();
        private bool initialized;

        private void LoadModules(List<string> modules)
        {
            List<Module> errorModules = new List<Module>();
            foreach (string module in modules)
            {
                var convertModule = ConvertToModule(module);
                if(convertModule != null)
                {
                    errorModules.Add((Module)convertModule);
                }
                
            }

            foreach (var module in errorModules)
            {
                IErrorTracking errorTracking = Reflector.Instance.CreateModules<IErrorTracking>(module);
                if (errorTracking == null)
                {
                    Log.E($"Load {module} failed!");
                    break;
                }
                errorTrackingMap[module] = errorTracking;
            }
        }

        public void Setup(List<string> modules)
        {
            if(initialized)
            {
                Log.I("Already initialized, skipping setup");
                return;
            }
            LoadModules(modules);
            
            Dictionary<string, string> tags = CreateBuildInTags();

            foreach(IErrorTracking errorTracking in errorTrackingMap.Values)
            {
                errorTracking.Setup();
                errorTracking.SetTags(tags);
            }

            initialized = true;
        }

        public void SetUser(UserModel user)
        {
            if(user != null)
            {
                foreach(IErrorTracking errorTracking in errorTrackingMap.Values)
                {
                    errorTracking.SetUser(user.comboId);
                    errorTracking.SetTags(new Dictionary<string, string>{ {Constants.IDP, user.idp}});
                }
            }
        }

        public void SetBreadcrumbs(List<EventBreadcrumb> breadcrumbs)
        {
            foreach(IErrorTracking errorTracking in errorTrackingMap.Values)
            {
                errorTracking.SetBreadcrumbs(breadcrumbs);
            }
        }

        public void CaptureMessage(string message, SentryLevel level, EventScope eventScope = null)
        {
            foreach(IErrorTracking errorTracking in errorTrackingMap.Values)
            {
                errorTracking.CaptureMessage(message, level, eventScope);
            }
        }

        public void CaptureException(Exception error, Action<EventScope> callback = null)
        {
            if(!ShouldTracking(error))
            {
                return;
            }
            var title = error.GetType().Name;
            if (error is InternalError ie && ie.Cause is BaseCause cause)
            {
                // BaseCause 名称
                title = cause.GetType().Name;

                // 如果是 ThirdPartyError 并且 moduleName 不为空，则追加模块名称
                if (cause is ThirdPartyError trdError && !string.IsNullOrEmpty(trdError.ModuleName))
                {
                    title += " | " + trdError.ModuleName;
                }
            }
            foreach(var errorTracking in errorTrackingMap.Values)
            {
                EventScope eventScope = new EventScope();
                callback?.Invoke(eventScope);

                if(error is InternalError internalError)
                {
                    eventScope.TagMap[Constants.COMBO_ERROR] = title;
                    eventScope.Fingerprint = new List<string>() {title};
                    errorTracking.CaptureMessage($"{title}\n{internalError.DetailMessage}", SentryLevel.Error, eventScope);
                }
                else
                {
                    eventScope.TagMap[Constants.COMBO_ERROR] = "UnInternalError";
                    errorTracking.CaptureException(error, eventScope);
                }
            }
        }

        private Dictionary<string, string> CreateBuildInTags()
        {
            Dictionary<string, string> tags = new Dictionary<string, string>
            {
                {Constants.SDK_WINDOWS_VERSION, Seayoo.ComboSDK.Windows.Version.Version.SDKVersion},
            };

            return tags;
        }

        private Module? ConvertToModule(string module)
        {
            switch (module)
            {
                case "sentry":
                    return Module.Sentry;
                default:
                    return null;
            }
        }

        private bool ShouldTracking(Exception error)
        {
            var isInternalError = error is InternalError ie;
            if (isInternalError)
            {
                ie = (InternalError)error;
            }
            else
            {
                return true;
            }

            var isBaseCause = ie != null && ie.Cause is BaseCause baseCause;
            if(isBaseCause)
            {
                baseCause = (BaseCause)ie.Cause;
            }
            else
            {
                return true;
            }

            return baseCause.EnableTracking;
        }
    }

    public static class Constants
    {
        // Tags
        public const string SDK_WINDOWS_VERSION = "combo.sdk.windows_sdk";
        public const string IDP = "combo.idp";
        public const string COMBO_ERROR = "combo.sdk.error";
        // Extra
        public const string MODULE_VERSIONS = "combo.sdk.modules";
    }
}