using Hotline.Authentications;
using Hotline.Caching.Interfaces;
using Hotline.Configurations;
using Hotline.Identity.Accounts;
using Hotline.Settings;
using Hotline.Share.Tools;
using IdentityModel;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using XF.Domain.Authentications;
using XF.Domain.Dependency;
using XF.Domain.Exceptions;
using XF.Domain.Repository;

namespace Hotline.Tests.Controller;

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

    public TestSessionContextManager(
        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)
            httpContextAccessor.HttpContext = new DefaultHttpContext()
            {
                RequestServices = _serviceProvider
            };
        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);

        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 = setting.FromJson<CityBaseConfiguration>();
        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)
    {
        throw new NotImplementedException();
    }

    #region private

    private void ChangeSession(string userId, string username, string orgId, string orgname, int orgLevel)
    {
        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(AppClaimTypes.DepartmentId, orgId ?? string.Empty),
            new(AppClaimTypes.DepartmentIsCenter, orgId?.IsCenter().ToString() ?? string.Empty),
            new(AppClaimTypes.DepartmentName, orgname ?? string.Empty),
            new(AppClaimTypes.DepartmentLevel, orgLevel.ToString() ?? string.Empty),
            new(AppClaimTypes.AreaId, orgId?.GetHigherOrgId() ?? string.Empty),
        ];
        httpContextAccessor.HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity(userClaims));
    }

    #endregion
}
