using System.Security.Claims;
using Hotline.Caching.Interfaces;
using Hotline.Configurations;
using Hotline.Identity.Accounts;
using Hotline.Settings;
using IdentityModel;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using XF.Domain.Authentications;
using XF.Domain.Dependency;
using XF.Domain.Exceptions;
using XF.Domain.Repository;

namespace Hotline.Authentications;

public interface ISessionContextManager
{
    Task ChangeSessionContextByUserIdAsync(string userId, CancellationToken cancellation);

    /// <summary>
    /// 依据source创建对应Session
    /// </summary>
    void ChangeSessionContext(string source);

    void ChangeSessionContext(ISessionContext sessionContext);
}

public class SessionContextManager : ISessionContextManager, IScopeDependency
{
    private readonly IServiceProvider _serviceProvider;
    private readonly ISystemSettingCacheManager _systemSettingCacheManager;

    public SessionContextManager(
        IServiceProvider serviceProvider,
        ISystemSettingCacheManager systemSettingCacheManager)
    {
        _serviceProvider = serviceProvider;
        _systemSettingCacheManager = systemSettingCacheManager;
    }

    public async Task ChangeSessionContextByUserIdAsync(string userId, CancellationToken cancellation)
    {
        var httpContextAccessor = _serviceProvider.GetRequiredService<IHttpContextAccessor>();
        if (httpContextAccessor.HttpContext == null)
            throw new UserFriendlyException("current httpContext is null");
        var accountRepository = _serviceProvider.GetService<IRepository<Account>>();

        var account = await accountRepository.Queryable()
            .Includes(d => d.User, x => x.Organization)
            .Includes(d => d.Roles)
            .FirstAsync(d => d.Id == userId, cancellation);

        ChangeSession(account.Id, account.Name,
            account.User?.OrgId, account.User?.Organization?.Name,
            account.User?.Organization?.Level,
            account.User?.Organization?.IsCenter,
            account.PhoneNo,
            account.User?.Organization?.AreaCode,
            account.User?.Organization?.AreaName,
            account.Roles.Select(d=>d.Name).ToList()
        );
        //
        // List<Claim> userClaims =
        // [
        //     new(JwtClaimTypes.Subject, account.Id),
        //     new(JwtClaimTypes.PhoneNumber, account.PhoneNo ?? string.Empty),
        //     new(ClaimTypes.NameIdentifier, account.Id),
        //     new(AppClaimTypes.UserDisplayName, account.Name),
        //     new(AppClaimTypes.DepartmentId, account.User?.OrgId ?? string.Empty),
        //     new(AppClaimTypes.DepartmentIsCenter, account.User?.Organization?.IsCenter.ToString() ?? string.Empty),
        //     new(AppClaimTypes.DepartmentName, account.User?.Organization?.Name ?? string.Empty),
        //     new(AppClaimTypes.DepartmentAreaCode, account.User?.Organization?.AreaCode ?? string.Empty),
        //     new(AppClaimTypes.DepartmentAreaName, account.User?.Organization?.AreaName ?? string.Empty),
        //     new(AppClaimTypes.DepartmentLevel, account.User?.Organization?.Level.ToString() ?? string.Empty),
        //     new(AppClaimTypes.AreaId, account.User?.OrgId?.GetHigherOrgId() ?? string.Empty),
        // ];
        // userClaims.AddRange(account.Roles.Select(d => new Claim(JwtClaimTypes.Role, d.Name)));
        // httpContextAccessor.HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity(userClaims));
    }

    /// <summary>
    /// 依据source创建对应Session
    /// </summary>
    public void ChangeSessionContext(string source)
    {
        var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
        var cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
        DefaultCityBaseConfiguration config = source switch
        {
            "province" => cityBase.CityProvinceAssign,
            "110" => cityBase.PublicSecurity,
            "yb-enterprise" => cityBase.CityEnterprise,
            "zzpt" => cityBase.ComprehensiveTreatment,
            _ => throw new ArgumentOutOfRangeException(nameof(source), source, null)
        };

        ChangeSession(config.UserId, config.UserName, config.OrgId, config.OrgName, config.OrgId.CalcOrgLevel());
    }

    public void ChangeSessionContext(ISessionContext sessionContext)
    {
        ChangeSession(sessionContext.UserId, sessionContext.UserName,
            sessionContext.OrgId, sessionContext.OrgName,
            sessionContext.OrgLevel, sessionContext.OrgIsCenter,
            sessionContext.Phone,
            sessionContext.OrgAreaCode, sessionContext.OrgAreaName,
            sessionContext.Roles.ToList());
    }

    #region private

    private void ChangeSession(
        string userId, string username,
        string orgId, string orgname,
        int? orgLevel, 
        bool? isCenter = null,
        string? phoneNo = null,
        string? orgAreaCode = null,
        string? orgAreaName = null,
        List<string>? roles = null)
    {
        var httpContextAccessor = _serviceProvider.GetRequiredService<IHttpContextAccessor>();
        if (httpContextAccessor.HttpContext == null)
            throw new UserFriendlyException("current httpContext is null");

        List<Claim> userClaims =
        [
            new(JwtClaimTypes.Subject, userId),
            new(ClaimTypes.NameIdentifier, userId),
            new(AppClaimTypes.UserDisplayName, username),
            new(JwtClaimTypes.PhoneNumber, phoneNo ?? string.Empty),
            new(AppClaimTypes.DepartmentId, orgId ?? string.Empty),
            new(AppClaimTypes.DepartmentName, orgname ?? string.Empty),
            new(AppClaimTypes.DepartmentIsCenter, isCenter.HasValue 
                ? isCenter.Value.ToString() 
                : (orgId?.IsCenter().ToString() ?? string.Empty)),
            new(AppClaimTypes.DepartmentAreaCode, orgAreaCode ?? string.Empty),
            new(AppClaimTypes.DepartmentAreaName, orgAreaName ?? string.Empty),
            new(AppClaimTypes.DepartmentLevel, orgLevel?.ToString() ?? string.Empty),
            new(AppClaimTypes.AreaId, orgId?.GetHigherOrgId() ?? string.Empty),
        ];
        if (roles != null && roles.Count > 0)
            userClaims.AddRange(roles.Select(d => new Claim(JwtClaimTypes.Role, d)));
        httpContextAccessor.HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity(userClaims));
    }

    #endregion
}