using System;
using System.Collections.Generic;
using System.Linq;
using _Combo.Events;
using _Core;

namespace _Combo
{
    internal class IdPManager : Singleton<IdPManager>
    {

        private Action<ComboResult<UserModel>> loginFinishedAction;
        private Action<ComboResult<bool>> kickOutAction;

        private Dictionary<Module, IdP> idpEntities = new Dictionary<Module, IdP>();

        private UserModel user = new UserModel();
        private Credential credential;
        private Credential linkCredential;
        private bool existLinkAccount;
        private List<string> setupIdpModules = new List<string>();

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

        public void Setup(List<string> modules, SetupOptions opts)
        {
            List<Module> idpModules = new List<Module>();
            foreach (string module in modules)
            {
                if (ConvertToModule(module) != null)
                {
                    idpModules.Add((Module)ConvertToModule(module));
                    if (!setupIdpModules.Contains(module))
                    {
                        setupIdpModules.Add(module);
                    }
                }
            }

            foreach (var idpModule in idpModules)
            {
                IdP entity = Reflector.Instance.CreateModules<IdP>(idpModule);
                if (entity == null)
                {
                    Log.E($"Load {idpModule} idp failed!");
                    break;
                }
                entity.Setup(opts);
                idpEntities[idpModule] = entity;
            }
        }

        public List<string> GetIdpModule()
        {
            return setupIdpModules;
        }

        public void Login()
        {
            if (idpEntities == null || idpEntities.Values == null || !idpEntities.Values.Any())
            {
                var error = new InternalError(new FeatureNotAvailableError("Login"));
                LoginFailedEvent.Invoke(new LoginFailedEvent { error = error });
                return;
            }
            idpEntities.First().Value.Login();
        }

        public void Login(Action<ComboResult<UserModel>> action)
        {
            loginFinishedAction = action;
            if (idpEntities == null || idpEntities.Values == null || !idpEntities.Values.Any())
            {
                var error = new InternalError(new FeatureNotAvailableError("Login"));
                LoginFailedEvent.Invoke(new LoginFailedEvent { error = error });
                return;
            }
            idpEntities.First().Value.Login();
        }

        public void Login(LocalIdP idp, Action<ComboResult<UserModel>> action)
        {
            loginFinishedAction = action;
            Module module = ConvertToModule(idp);

            if (idpEntities.TryGetValue(module, out IdP idpEntity))
            {
                idpEntity.Login();
            }
            else
            {
                Log.E($"Login failed: {module} don't have idp entity!");
            }
        }

        public void Logout()
        {
            var idp = AttributeUtils.GetIdp(credential);
            var module = ConvertToIdp(idp);
            if (idpEntities.TryGetValue((Module)module, out IdP idpEntity))
            {
                idpEntity.Logout();
            }
            this.user.Clear();
            CancelAntiAddictionTimer();
        }

        public void KickOut(Action<ComboResult<bool>> action)
        {
            kickOutAction = action;
        }

        public void KickOut(bool shouldExit)
        {
            Logout();
            kickOutAction?.Invoke(ComboResult<bool>.CreateSuccess(shouldExit));
        }

        public void Logout(Action<ComboResult<UserModel>> action)
        {
            var idp = AttributeUtils.GetIdp(credential);
            var module = ConvertToIdp(idp);
            if (idpEntities.TryGetValue((Module)module, out IdP idpEntity))
            {
                idpEntity.Logout();
            }
            var userInfo = new UserModel()
            {
                comboId = user.comboId,
                idp = idp,
                identityToken = user.identityToken,
                externalId = user.externalId,
                externalName = user.externalName,
                expiresAt = user.expiresAt,
                realNameRequired = user.realNameRequired,
                realNameTicket = user.realNameTicket,
            };
            this.user.Clear();
            CancelAntiAddictionTimer();
            action.Invoke(ComboResult<UserModel>.CreateSuccess(userInfo));
        }

        public UserModel GetUser()
        {
            return user;
        }

        public bool IsFeatureAvailable(Feature feature)
        {
            switch (feature)
            {
                case Feature.SEAYOO_ACCOUNT:
                    return credential is SeayooCredential && idpEntities[Module.SeayooAccount].IsFeatureAvailable(feature);
                case Feature.CONTACT_SUPPORT:
                    return idpEntities.Values.Any(idProvider => idProvider.IsFeatureAvailable(feature));
                default:
                    return false;
            }
        }

        public void IdentityExists(string idp, string externalId, Credential credential)
        {
            this.credential = credential;
            ComboClient.IdentityExists(idp, externalId, result =>
            {
                if (result.IsSuccess)
                {
                    var data = result.Data;
                    existLinkAccount = data.exists;
                    if (data.exists)
                    {
                        LoginSuccessEvent.Invoke(new LoginSuccessEvent() { credential = credential });
                    }
                    else
                    {
                        idpEntities.First().Value.LinkIdentity(credential);
                    }
                }
            }, () =>
            {
                CloseLoginViewEvent.Invoke();
                Logout();
            });
        }

        public void LinkIdentity(string identityToken, string idp, Credential linkCredential)
        {
            ComboClient.LinkIdentity(identityToken, idp, linkCredential, result =>
            {
                if (result.IsSuccess)
                {
                    LoginSuccessFinish(credential);
                    var linkIdp = AttributeUtils.GetIdp(linkCredential);
                    var module = ConvertToIdp(linkIdp);
                    if (module == null)
                    {
                        var error = new InternalError(new FeatureNotAvailableError("Login"));
                        LoginFailedEvent.Invoke(new LoginFailedEvent { error = error });
                        return;
                    }
                    if (idpEntities.TryGetValue((Module)module, out IdP idpEntity))
                    {
                        idpEntity.Logout();
                    }
                }
            }, (err) =>
            {
                ErrorTrackingManager.Instance.CaptureException(err);
                var userId = user.externalId;
                var serverError = ErrorExtensions.Convert<ServerError>(err);
                if (serverError == null)
                    return;
                var serverErrorCode = serverError.Error ?? "";
                UIController.Instance.ShowAlertView(
                    I18n.T("common_dialog_title"),
                    serverError.ErrorMsg,
                    UnityEngine.TextAnchor.UpperLeft,
                    I18n.T("acknowledge_button"),
                    () =>
                    {
                        Logout();
                        Login();
                        DeleteUserHistoryEvent.Invoke(new DeleteUserHistoryEvent
                        {
                            userId = userId
                        });
                    }
                );
                this.user.Clear();
            });
        }

        #region EVENT
        [EventSystem.BindEvent]
        public void LoginSuccess(LoginSuccessEvent e)
        {
            credential = e.credential;
            linkCredential = e.linkCredential;
            if (!(credential is SeayooCredential))
            {
                UIController.Instance.ShowLoading(0.5f);
            }
            ComboClient.SignIn(e.credential, resp =>
            {
                UIController.Instance.HideLoading();
                user = resp.Data;

                if (ShouldLink())
                {
                    if (user.activationRequired)
                    {
                        UIController.Instance.ShowActivationView(user.activationTicket);
                        return;
                    }
                    LinkIdentity(user.identityToken, AttributeUtils.GetIdp(e.linkCredential), e.linkCredential);
                    return;
                }
                if (user.fangchenmiTtl != null)
                {
                    UIController.Instance.ShowAlertView(
                    I18n.T("login_fangchenmi_dialog_title"),
                    I18n.T("fangchenmi_time_dialog_text"),
                    UnityEngine.TextAnchor.UpperLeft,
                    I18n.T("acknowledge_button"),
                    () =>
                    {
                        LoginSuccessFinish(credential);
                    }
                    );
                    StartAntiAddictionTimer((int)user.fangchenmiTtl);
                }
                else
                {
                    LoginSuccessFinish(credential);
                }
            }, (err) =>
            {
                UIController.Instance.HideLoading();
                ErrorTrackingManager.Instance.CaptureException(err);
                var serverError = ErrorExtensions.Convert<ServerError>(err);
                if (serverError == null)
                    return;
                var serverErrorCode = serverError.Error ?? "";

                if (serverErrorCode == "game_age_restricted" || serverErrorCode == "fangchenmi_time_restricted" || serverErrorCode == "fangchenmi_age_restricted" || serverErrorCode == "access_denied")
                {
                    string title = I18n.T("common_dialog_title");
                    if (serverErrorCode == "game_age_restricted" || serverErrorCode == "fangchenmi_time_restricted" || serverErrorCode == "fangchenmi_age_restricted")
                    {
                        title = I18n.T("login_fangchenmi_dialog_title");
                    }
                    else if (serverErrorCode == "access_denied")
                    {
                        title = I18n.T("login_access_denied_dialog_title");
                    }
                    UIController.Instance.ShowAlertView(
                        title,
                        serverError.ErrorMsg.Replace(" ", "\u00A0"),
                        UnityEngine.TextAnchor.UpperLeft,
                        I18n.T("acknowledge_button"),
                        () =>
                        {
                            Logout();
                            Login();
                        }
                    );
                }
                else
                {
                    UIController.Instance.ShowToast(serverError.ErrorMsg);
                    Logout();
                    Login();
                }
            });
        }

        [EventSystem.BindEvent]
        public void LoginCancel(LoginCancelEvent e)
        {
            var error = new InternalError(I18n.T("user_cancelled_error"), new UserCancelled());
            loginFinishedAction?.Invoke(ComboResult<UserModel>.CreateFail(error));
        }

        [EventSystem.BindEvent]
        public void LoginFailed(LoginFailedEvent e)
        {
            ErrorTrackingManager.Instance.CaptureException(e.error);
            loginFinishedAction?.Invoke(ComboResult<UserModel>.CreateFail(e.error));
        }

        [EventSystem.BindEvent]
        public void CheckLinkIdentity(CheckLinkIdentityEvent e)
        {
            IdentityExists(AttributeUtils.GetIdp(e.credential), e.externalId, e.credential);
        }

        [EventSystem.BindEvent]
        public void LinkIdentityFailed(LinkIdentityFailedEvent e)
        {
            var idp = AttributeUtils.GetIdp(e.linkCredential);
            var module = ConvertToIdp(idp);
            if (idpEntities.TryGetValue((Module)module, out IdP idpEntity))
            {
                idpEntity.Logout();
            }
        }
        #endregion EVENT

        private void SignInCompletion(Credential credential)
        {
            var idp = AttributeUtils.GetIdp(credential);
            var module = ConvertToIdp(idp);

            if (idpEntities.TryGetValue((Module)module, out IdP idpEntity))
            {
                idpEntity.OnSignIn(true);
            }
        }


        private Module ConvertToModule(LocalIdP idp)
        {
            switch (idp)
            {
                case LocalIdP.Seayoo:
                    return Module.SeayooAccount;
                case LocalIdP.TapTap:
                    return Module.TapTap;
                case LocalIdP.Lenovo:
                    return Module.Lenovo;
                default:
                    return Module.SeayooAccount;
            }
        }

        private Module? ConvertToModule(string module)
        {
            switch (module)
            {
                case "seayoo_account":
                    return Module.SeayooAccount;
                case "taptap":
                    return Module.TapTap;
                case "lenovo_pc":
                    return Module.Lenovo;
                default:
                    return null;
            }
        }

        private Module? ConvertToIdp(string idp)
        {
            switch (idp)
            {
                case "seayoo":
                    return Module.SeayooAccount;
                case "taptap":
                    return Module.TapTap;
                case "lenovo":
                    return Module.Lenovo;
                default:
                    return null;
            }
        }

        // 激活码激活
        public void ActivatePlayer(string key, Action<bool> callback)
        {
            var ticket = user.activationTicket;
            UIController.Instance.ShowLoading(0.5f);
            ComboClient.ActivatePlayer(ticket, key, result =>
            {
                callback.Invoke(true);
                UIController.Instance.HideLoading();
                var data = result.Data;
                user.identityToken = data.identityToken;
                user.expiresAt = data.expiresAt;
                user.activationRequired = false;
                loginFinishedAction.Invoke(ComboResult<UserModel>.CreateSuccess(user));
                ErrorTrackingManager.Instance.SetUser(user);
                SignInCompletion(credential);
                if (ShouldLink())
                {
                    LinkIdentity(user.identityToken, AttributeUtils.GetIdp(linkCredential), linkCredential);
                    return;
                }
                LoginSuccessFinish(credential);
            }, err =>
            {
                callback.Invoke(false);
                UIController.Instance.HideLoading();
                Log.E($"ActivatePlayer failed: {err}");
                ErrorTrackingManager.Instance.CaptureException(err);
                UIController.Instance.ShowToast(err.Message);
            });
        }

        private bool ShouldLink()
        {
            return (linkCredential != null && !(linkCredential is SeayooCredential) && !existLinkAccount);
        }

        private void LoginSuccessFinish(Credential credential)
        {

            void LoginSucceed()
            {
                loginFinishedAction.Invoke(ComboResult<UserModel>.CreateSuccess(user));
                ErrorTrackingManager.Instance.SetUser(user);
                SignInCompletion(credential);
            }

            if (user.activationRequired)
            {
                UIController.Instance.ShowActivationView(user.activationTicket);
                return;
            }

            if (credential is SeayooCredential seayooCredential)
            {
                if (GlobalParameters.Instance.SeayooAccountHideAccountSwitcher)
                {
                    LoginSucceed();
                }
                else
                {
                    UIController.Instance.ShowWelcomeView(seayooCredential.mobile, () =>
                    {
                        Logout();
                        Login();
                    }, LoginSucceed, credential);
                }
            }
            else if (credential is TaptapCredential taptapCredential)
            {
                CloseLoginViewEvent.Invoke();
                if (GlobalParameters.Instance.SeayooAccountHideAccountSwitcher)
                {
                    LoginSucceed();
                }
                else
                {
                    UIController.Instance.ShowWelcomeView(taptapCredential.name, () =>
                    {
                        Logout();
                        Login();
                    }, LoginSucceed, credential);
                }
            }
            else if (credential is LenovoCredential lenovoCredential)
            {
                if (GlobalParameters.Instance.SeayooAccountHideAccountSwitcher)
                {
                    LoginSucceed();
                }
                else
                {
                    UIController.Instance.ShowWelcomeView($"{user.externalName.Substring(0, 3)}******{user.externalName.Substring(9)}", () =>
                    {
                        Logout();
                        Login();
                    }, LoginSucceed, credential);
                }
            }
        }

        private void StartAntiAddictionTimer(int time)
        {
            var key = "fangchenmiTtl";
            var countdownTimer = CountdownTimer.GetInstance(key, time);
            countdownTimer.SubscribeOnCountdownFinished(key, OnCountdownFinished);
            countdownTimer.Start();
        }

        private void CancelAntiAddictionTimer()
        {
            var key = "fangchenmiTtl";
            var countdownTimer = CountdownTimer.GetInstance(key);
            if (countdownTimer == null)
            {
                return;
            }
            countdownTimer.UnsubscribeOnCountdown(key, OnCountdownFinished);
        }

        private void OnCountdownFinished()
        {
            UIController.Instance.ShowAlertView(
                    I18n.T("login_fangchenmi_dialog_title"),
                    I18n.T("fangchenmi_time_dialog_text"),
                    UnityEngine.TextAnchor.UpperLeft,
                    I18n.T("acknowledge_button"),
                    () =>
                    {
                        KickOut(false);
                    },
                    10
            );
        }
    }
}
