using System;
using System.Collections.Generic;
using System.Linq;
using Aitex.Core.RT.DBCore;
using Aitex.Core.Util;

namespace Aitex.Core.Account;

/// <summary>
/// 登录凭据管理器。
/// </summary>
public class CredentialManager : Singleton<CredentialManager>
{
    #region Variables

    /// <summary>
    /// 已登录的凭据激活超时时间。
    /// </summary>
    /// <remarks>
    /// 如果凭据超过此时间没有被激活，则自动移除此凭据。
    /// </remarks>
    public const int LOGIN_CRED_KEEP_ALIVE_TIMEOUT_SEC = 60;

    /// <summary>
    /// 正在请求登录的凭据的生命时长。
    /// </summary>
    public const int REQ_LOGIN_CRED_LIFT_TIME_SEC = REQ_LOGIN_DIALOG_LIFT_TIME_SEC + 5;

    /// <summary>
    /// UI中的请求登录/授权登录对话框存活时间
    /// </summary>
    public const int REQ_LOGIN_DIALOG_LIFT_TIME_SEC = 30;



    private readonly object _syncRoot = new();
    
    /// <summary>
    /// 已登录的凭据。
    /// </summary>
    private readonly Dictionary<Guid, Credential> _dictCredentialsLoggedIn = new ();
    
    /// <summary>
    /// 正在等在登录请求确认的凭据，LoginName为字典Key。
    /// </summary>
    private readonly Dictionary<string, Credential> _dictCredentialsRequesting = new ();
    
    private readonly PeriodicJob _threadMonitorCred;
    private bool _isInitialized;
    private int _maxCredentialAllowed;

    #endregion

    #region Constructors

    /// <summary>
    /// 登录凭据管理的构造函数。
    /// </summary>
    public CredentialManager()
    {
        _threadMonitorCred = new(1000, OnTimer, "CredentialAliveMonitorThread", true, true);
    }
    
    #endregion

    #region Properties

    /// <summary>
    /// 返回当前凭据管理器是否支持多用户登录。
    /// </summary>
    public bool IsSupportMultiUserLogin => _maxCredentialAllowed > 1;

    /// <summary>
    /// 返回已登录的用户凭据的数量。
    /// </summary>
    public int LoggedInCount
    {
        get
        {
            lock (_syncRoot)
            {
                return _dictCredentialsLoggedIn.Count;
            }
        }
    }

    /// <summary>
    /// 返回正在请求登录的用户凭据的数量。
    /// </summary>
    public int LoginRequestingCount
    {
        get
        {
            lock (_syncRoot)
            {
                return _dictCredentialsRequesting.Count;
            }
        }
    }

    #endregion

    #region Methods

    /// <summary>
    /// 初始化登录凭据管理器。
    /// </summary>
    /// <param name="isSupportMultiUsersLogin">是否支持多用户同时登录。</param>
    public void Initialize(bool isSupportMultiUsersLogin)
    {
        if (_isInitialized)
            throw new InvalidOperationException($"{nameof(CredentialManager)} has been initialized.");

        _isInitialized = true;
        _maxCredentialAllowed = isSupportMultiUsersLogin ? int.MaxValue : 1;
    }

    private bool OnTimer()
    {
        lock (_syncRoot)
        {
            #region 已登录凭据存活检测

            var loginRemovableList = new List<Guid>();
            foreach (var kvp in _dictCredentialsLoggedIn)
            {
                var cred = kvp.Value;
                if ((DateTime.Now - cred.LastAliveTime).TotalSeconds > LOGIN_CRED_KEEP_ALIVE_TIMEOUT_SEC)
                    loginRemovableList.Add(cred.Token);
            }

            if (loginRemovableList.Count > 0)
            {
                foreach (var token in loginRemovableList)
                {
                    WriteHistory(_dictCredentialsLoggedIn[token], "EXPIRED");
                    _dictCredentialsLoggedIn.Remove(token);
                }
            }

            #endregion

            #region 请求登录凭据存活检测

            var requestRemovableList = new List<string>();
            foreach (var kvp in _dictCredentialsRequesting)
            {
                var cred = kvp.Value;
                if ((DateTime.Now - cred.LastAliveTime).TotalSeconds > REQ_LOGIN_CRED_LIFT_TIME_SEC)
                    requestRemovableList.Add(kvp.Key);

                // 移除被取消或拒绝的凭据
                if(cred.State is CredentialState.RequestCanceled or CredentialState.Reject)
                    requestRemovableList.Add(kvp.Key);
            }

            if (requestRemovableList.Count > 0)
            {
                foreach (var loginName in requestRemovableList)
                {
                    var reason = "";
                    switch (_dictCredentialsRequesting[loginName].State)
                    {
                        case CredentialState.RequestCanceled:
                          reason = "REQ CANCELED";
                            break;
                        case CredentialState.Reject:
                            reason = "REQ REJECTED";
                            break;
                        default:
                            reason = "REQ EXPIRED";
                            break;
                    }
                    WriteHistory(_dictCredentialsRequesting[loginName], reason);
                    _dictCredentialsRequesting.Remove(loginName);
                }
            }

            return true;

            #endregion
        }
    }

    /// <summary>
    /// 检查指定的用户名是否已经登录。
    /// </summary>
    /// <param name="userName"></param>
    /// <returns></returns>
    internal bool IsLoggedIn(string userName)
    {
        lock (_syncRoot)
        {
            return _dictCredentialsLoggedIn.Values.FirstOrDefault(x => x.AccountInfo.LoginName == userName) != null;
        }
    }

    /// <summary>
    /// 检查指定令牌的登录凭据是否存在。
    /// </summary>
    /// <param name="token"></param>
    /// <returns></returns>
    internal bool IsLoggedIn(Guid token)
    {
        lock (_syncRoot)
        {
            return _dictCredentialsLoggedIn.ContainsKey(token);
        }
    }

    /// <summary>
    /// 检查指定的令牌是否已经过期。
    /// </summary>
    /// <param name="token"></param>
    /// <returns></returns>
    internal bool IsTokenExpired(Guid token)
    {
        lock (_syncRoot)
        {
            return _dictCredentialsLoggedIn.ContainsKey(token);
        }
    }

    /// <summary>
    /// 报告客户端处于活动状态。
    /// </summary>
    /// <param name="myToken">客户端登录凭据</param>
    /// <returns></returns>
    public CredentialKeepAliveCheckResult KeepAlive(Guid myToken)
    {
        lock (_syncRoot)
        {
            if (_dictCredentialsLoggedIn.TryGetValue(myToken, out var loginCred))
            {
                loginCred.LastAliveTime = DateTime.Now; // 刷新时间

                if (IsSupportMultiUserLogin)
                {
                    // 如果当前用户名在请求登录列表中，则返回CredentialKeepAliveResults.RequestingLogin，通知
                    // 已登录的客户端，当前用户正在请求异地登录。
                    if (_dictCredentialsRequesting.TryGetValue(loginCred.AccountInfo.LoginName,
                            out var requestingLoginCred) && requestingLoginCred.State == CredentialState.Requesting)
                    {
                        return new(CredentialKeepAliveStates.RequestingLogin,
                            requestingLoginCred);
                    }
                }
                else
                {
                    // 如果仅允许单一用户登录，并且登录请求列表中有等待确认的凭据，则通知当前凭据的Session弹出确认对话框
                    if (LoginRequestingCount > 0)
                    {
                        var requestingLoginCred = _dictCredentialsRequesting.Values.FirstOrDefault();
                        if (requestingLoginCred != null)

                            return new(CredentialKeepAliveStates.RequestingLogin,
                                requestingLoginCred);
                    }
                }

                return new (CredentialKeepAliveStates.Alive);
            }
            
            return new (CredentialKeepAliveStates.NotFound);
        }
    }

    /// <summary>
    /// 将凭据加入请求列表。
    /// </summary>
    /// <param name="cred"></param>
    /// <exception cref="InvalidOperationException"></exception>
    public void AddRequestingList(Credential cred)
    {
        lock (_syncRoot)
        {
            if (IsSupportMultiUserLogin)
            {
                if (_dictCredentialsRequesting.ContainsKey(cred.AccountInfo.LoginName))
                    throw new InvalidOperationException("the credential has been existed in requesting list.");
            }
            else
            {
                if (LoginRequestingCount > 0)
                {
                    var credRequesting = _dictCredentialsRequesting.Values.FirstOrDefault();
                    throw new InvalidOperationException($"User {credRequesting} are requesting to login, new request is not allowed.");
                }
            }

            WriteHistory(cred, "REQ LOGIN");
            _dictCredentialsRequesting[cred.AccountInfo.LoginName] = cred;
        }
    }

    /// <summary>
    /// 将指定的凭据加入字典。
    /// </summary>
    /// <param name="cred">待授权的凭据</param>
    /// <remarks>
    /// 给凭据授权前必须调用<see cref="AddRequestingList"/>方法将凭据加入到等待列表中。
    /// </remarks>
    /// <exception cref="Exception"></exception>
    public void Grant(Credential cred)
    {
        if (IsLoggedIn(cred.AccountInfo.LoginName))
            throw new Exception($"user {cred.AccountInfo.LoginName} has been logged in.");

        lock (_syncRoot)
        {
            if (_dictCredentialsLoggedIn.Count >= _maxCredentialAllowed)
                throw new InvalidOperationException("maximum number of login credentials reached");

            if (!_dictCredentialsRequesting.ContainsKey(cred.AccountInfo.LoginName))
                throw new InvalidOperationException("the credential is not found in requesting list.");
            
            cred.State = CredentialState.Alive;
            _dictCredentialsLoggedIn[cred.Token] = cred;
            _dictCredentialsRequesting.Remove(cred.AccountInfo.LoginName);

            WriteHistory(cred, "LOGIN");
        }
    }


    /// <summary>
    /// 强制放行登录授权，绕过Single-Session机制。
    /// </summary>
    /// <param name="cred"></param>
    public void ForceGrant(Credential cred)
    {
        lock (_syncRoot)
        {
            if (_dictCredentialsLoggedIn.Count >= _maxCredentialAllowed)
                throw new InvalidOperationException("maximum number of login credentials reached");

            cred.State = CredentialState.Alive;
            _dictCredentialsLoggedIn[cred.Token] = cred;

            WriteHistory(cred, "LOGIN");
        }
    }

    /// <summary>
    /// 接受指定用户名的登录请求。
    /// </summary>
    /// <param name="userName"></param>
    public void Accept(string userName)
    {
        var cred = GetRequestingCredential(userName);
        if (cred != null)
            cred.State = CredentialState.Confirmed;
    }

    /// <summary>
    /// 拒绝指定用户名的登录请求。
    /// </summary>
    /// <param name="userName"></param>
    public void Reject(string userName)
    {
        lock (_syncRoot)
        {
            var cred = GetRequestingCredential(userName);
            if (cred != null)
                cred.State = CredentialState.Reject;
        }
    }

    /// <summary>
    /// 取消登录请求。
    /// </summary>
    /// <param name="userName"></param>
    public void Cancel(string userName)
    {
        var cred = GetRequestingCredential(userName);
        if (cred != null)
        {
            cred.LoginRequestCancellationTokenSource.Cancel();
            cred.State = CredentialState.RequestCanceled;
        }
    }

    /// <summary>
    /// 移除指定令牌的凭据。
    /// </summary>
    /// <param name="token"></param>
    public void Remove(Guid token)
    {
        lock (_syncRoot)
        {
            if (_dictCredentialsLoggedIn.TryGetValue(token, out var cred))
            {
                WriteHistory(cred, "LOGOUT");
            }

            _dictCredentialsLoggedIn.Remove(token);

        }
    }

    /// <summary>
    /// 获取字典中第一个凭据。
    /// </summary>
    /// <returns></returns>
    public Credential GetCredential()
    {
        lock (_syncRoot)
        {
            return _dictCredentialsLoggedIn.Values.FirstOrDefault();
        }
    }

    /// <summary>
    /// 获取指定用户名的登录凭据。
    /// </summary>
    /// <param name="userName"></param>
    /// <returns></returns>
    public Credential GetCredential(string userName)
    {
        lock (_syncRoot)
        {
            return _dictCredentialsLoggedIn.Values.FirstOrDefault(x => x.AccountInfo.LoginName == userName);
        }
    }
    
    /// <summary>
    /// 获取指定用户名的登录凭据。
    /// </summary>
    /// <param name="token"></param>
    /// <returns></returns>
    public Credential GetCredential(Guid token)
    {
        lock (_syncRoot)
        {
            return _dictCredentialsLoggedIn.TryGetValue(token, out var cred) ? cred : null;
        }
    }

    /// <summary>
    /// 获取指定用户名的正在等在登录请求的凭据。
    /// </summary>
    /// <param name="userName">用户名</param>
    /// <returns></returns>
    public Credential GetRequestingCredential(string userName)
    {
        lock (_syncRoot)
        {
            return _dictCredentialsRequesting.TryGetValue(userName, out var cred) ? cred : null;
        }
    }

    /// <summary>
    /// 校验指定令牌的凭据是否有效。
    /// </summary>
    /// <param name="token"></param>
    /// <returns></returns>
    public bool ValidateCredential(Guid token)
    {
        lock (_syncRoot)
        {
            if (_dictCredentialsLoggedIn.TryGetValue(token, out var cred))
            {
                return cred.State == CredentialState.Alive;
            }

            return false;
        }
    }
    
    #endregion

    #region Static Methods

    private static void WriteHistory(Credential cred, string operation)
    {
        DB.InsertSql(
            "insert into credentials_history (\"login_name\", \"role_id\", \"host_name\", \"host_ip\", \"host_port\", " +
            "\"os_version\", \"computer_info\",  \"cpu_info\", \"disk_info\", \"operation\", \"operation_time\") " +
            "values" +
            $"($sic${cred.AccountInfo.LoginName}$sic$, $sic${cred.RoleID}$sic$, $sic${cred.ClientInfo?.HostName??""}$sic$, " +
            $"$sic${cred.LoginIP}$sic$, $sic${cred.LoginPort}$sic$, $sic${cred.ClientInfo?.OSVersion??""}$sic$," +
            $"$sic${cred.ClientInfo?.ComputerSystem??""}$sic$, $sic${cred.ClientInfo?.CpuInfo??""}$sic$, $sic${cred.ClientInfo?.LogicalDisk??""}$sic$, " +
            $"$sic${operation}$sic$, NOW())");
    }

    /// <summary>
    /// 创建一个令牌。
    /// </summary>
    /// <returns></returns>
    public static Guid GenerateToken()
    {
        return Guid.NewGuid();
    }

    #endregion
}