using System.Security.Claims;
using AutoFixture;
using Hotline.Api.Controllers;
using Hotline.Identity.Accounts;
using Hotline.Identity.Roles;
using Hotline.Settings;
using Hotline.Share.Dtos.Users;
using Hotline.Share.Enums.Order;
using Hotline.Share.Enums.ThirdAccount;
using Hotline.Share.Enums.User;
using Hotline.Share.Tools;
using Hotline.Snapshot.IRepository;
using Hotline.Tests.Infrastructure;
using Hotline.ThirdAccountDomainServices;
using Hotline.ThirdAccountDomainServices.Interfaces;
using Hotline.Users;
using IdentityModel;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using XF.Domain.Authentications;
using XF.Domain.Cache;
using XF.Domain.Repository;

namespace Hotline.Tests;
public class TestBase
{
    public readonly IAccountRepository _accountRepository;
    public readonly IRepository<User> _userRepository;
    public readonly IRepository<Role> _roleRepository;
    public readonly UserController _userController;
    public readonly IFixture _fixture;
    private readonly IServiceScopeFactory _scopeFactory;
    public readonly IHttpContextAccessor _httpContextAccessor;
    public readonly IThirdIdentiyService _thirdIdentiyService;
    public readonly IThirdAccountRepository _thirdAccountRepository;
    private readonly ITypedCache<SystemSetting> _cacheSettingData;
    public readonly ThirdAccounSupplierFactory _thirdAccountDomainFactory;
    public readonly IServiceProvider _serviceProvider;

    public TestBase(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, IThirdIdentiyService thirdIdentiyService, IThirdAccountRepository thirdAccountRepository, ITypedCache<SystemSetting> cacheSettingData, ThirdAccounSupplierFactory thirdAccountDomainFactory, IServiceProvider serviceProvider)
    {
        _thirdAccountRepository = thirdAccountRepository;
        _thirdIdentiyService = thirdIdentiyService;
        _fixture = new Fixture();
        _accountRepository = accountRepository;
        _roleRepository = roleRepository;
        _userController = userController;
        _userController.ControllerContext = new ControllerContext
        {
            HttpContext = new DefaultHttpContext
            {
                RequestServices = _serviceProvider
            }
        };
        _scopeFactory = scopeFactory;
        _userRepository = userRepository;
        _httpContextAccessor = httpContextAccessor;

        if (httpContextAccessor.HttpContext is null)
        {
            httpContextAccessor.HttpContext = new DefaultHttpContext
            {
                RequestServices = _serviceProvider
            };
            SetZuoXi();
        }

        _cacheSettingData = cacheSettingData;
        _thirdAccountDomainFactory = thirdAccountDomainFactory;
        _serviceProvider = serviceProvider;
    }

    public void SetSettingCache(string code, string value)
    {
        _cacheSettingData.Remove(code);
        var a = new SystemSetting { SettingValue = new List<string> { value } };
        _cacheSettingData.Set(code, a);
    }

    public void ChangeAppScopeYiBin()
    {
        ChangeAppScope("YiBin");
    }

    public void ChangeAppScope(string appScope)
    {
        var changeDto = _httpContextAccessor.HttpContext.User;
        List<Claim> userClaims = [
            new(JwtClaimTypes.Subject, changeDto.FindFirstValue(JwtClaimTypes.Subject)),
            new(JwtClaimTypes.PhoneNumber, changeDto.FindFirstValue(JwtClaimTypes.PhoneNumber)),
            new(ClaimTypes.NameIdentifier, changeDto.FindFirstValue(ClaimTypes.NameIdentifier)),
            new(AppClaimTypes.UserDisplayName, changeDto.FindFirstValue(AppClaimTypes.UserDisplayName)),
            new(AppClaimTypes.DepartmentId, changeDto.FindFirstValue(AppClaimTypes.DepartmentId)),
            new(AppClaimTypes.DepartmentIsCenter, changeDto.FindFirstValue(AppClaimTypes.DepartmentIsCenter)),
            new(AppClaimTypes.DepartmentName, changeDto.FindFirstValue(AppClaimTypes.DepartmentName)),
            new(AppClaimTypes.DepartmentAreaCode, changeDto.FindFirstValue(AppClaimTypes.DepartmentAreaCode)),
            new(AppClaimTypes.DepartmentAreaName, changeDto.FindFirstValue(AppClaimTypes.DepartmentAreaName)),
            new(AppClaimTypes.DepartmentLevel, changeDto.FindFirstValue(AppClaimTypes.DepartmentLevel)),
            new(AppClaimTypes.AreaId, changeDto.FindFirstValue(AppClaimTypes.AreaId)),
            new(AppClaimTypes.OpenId, changeDto.FindFirstValue(AppClaimTypes.OpenId)),
            new("AppScope", appScope)
        ];
        ClaimsIdentity identity = new ClaimsIdentity(userClaims);
        var principal = new ClaimsPrincipal(identity);
        _httpContextAccessor.HttpContext.User = principal;
    }

    public void ChangeAppScopeZiGong()
    {
        ChangeAppScope("ZiGong");
    }

    public void SetPaiDanYuan()
    {
        SetOperator("派单员", "市民热线服务中心", "单元测试派单员", "001", "13408389849", EUserType.Normal, TestSettingConstants.PaiDanYuanAccountName);
    }

    public void Set一级部门()
    {
        SetOperator("部门经办人", "测试部门", "测试", "001094", "13408389849", EUserType.Seat, TestSettingConstants.FirstOrgAccountName);
    }

    public void Set二级部门()
    {
        SetOperator("部门经办人", "测试部门/测试二级部门", "cs21", "001094001", "13408389849", EUserType.Normal, TestSettingConstants.SecondOrgAccountName);
    }

    public void Set班长()
    {
        SetOperator("中心班长", "市民热线服务中心", "单元测试班长", "001", "13408389849", EUserType.Normal, TestSettingConstants.BanZhangAccountName);
    }

    public void SetZuoXi()
    {
        SetOperator("坐席", "市民热线服务中心", "单元测试坐席", "001", "13408389849", EUserType.Seat, TestSettingConstants.ZuoXiAccountName);
    }

    public void Set政法委()
    {
        SetOperator("区县政法委", "自流井区人民政府/自流井区政法委", "单元测试政法委", "001055048", "13408389849", EUserType.Normal, TestSettingConstants.ZFWAccountName);
    }

    public void Set应急管理局()
    {
        SetOperator("二级部门坐席", "自流井区应急管理局", "单元测试应急管理局", "001055021", "13408389849", EUserType.Normal, TestSettingConstants.YJGLJAccountName);
    }

    public void SetWeiXin()
    {
        SetOperator("微信用户", "", "", "", "138001389877", EUserType.Normal, TestSettingConstants.WeiXinAccountName, EThirdType.WeChat, EAppType.Snapshot);
    }

    public void Set网格员()
    {
        SetOperator("网格员", "市民热线服务中心", "单元测试网格员", "001", "13408389849", EUserType.Normal, TestSettingConstants.GuiderAccountName);
    }

    private void SetOperator(string displayName, string fullOrgName, string name, string orgId, string phoneNo, EUserType userType, string userName, EThirdType? thirdType = null, EAppType? appType = null)
    {
        var account = _accountRepository.GetExtAsync(
            d => d.UserName == userName,
            d => d.Includes(x => x.Roles)).GetAwaiter().GetResult();

        var roleId = _roleRepository.Queryable()
                       .Where(m => m.DisplayName == displayName)
                       .Select(m => m.Id)
                       .First();

        if (account == null && roleId != null)
        {
            var newUser = new AddUserDto
            {
                FullOrgName = fullOrgName,
                Gender = EGender.Male,
                Name = name,
                OrgId = orgId,
                PhoneNo = phoneNo,
                RoleIds = [roleId],
                UserType = userType,
                UserName = userName
            };
            var accountId = _userController.Add(newUser).GetAwaiter().GetResult();
            account = _accountRepository.GetExtAsync(
                d => d.UserName == userName,
                d => d.Includes(x => x.Roles)).GetAwaiter().GetResult();
        }
        var user = _userRepository.Queryable()
            .Includes(d => d.Organization)
            .FirstAsync(d => d.Id == account.Id).GetAwaiter().GetResult();
        var third = _thirdIdentiyService.GetTokenAsync(new Share.Dtos.Snapshot.ThirdTokenDto(), CancellationToken.None).GetAwaiter().GetResult();
        var thirdAccount = _thirdAccountRepository.Get(d => d.OpenId == third.OpenId);

        var appScope = "ZiGong";
        if (_httpContextAccessor != null && _httpContextAccessor.HttpContext != null)
        {
            var m = _httpContextAccessor.HttpContext.User.FindFirstValue("AppScope");
            if (m != null)
                appScope = m;
        }

        List<Claim> userClaims = [
            new(JwtClaimTypes.Subject, account.Id ?? thirdAccount.Id),
            new(JwtClaimTypes.PhoneNumber, account.PhoneNo ?? thirdAccount.PhoneNumber),
            new(AppClaimTypes.UserDisplayName, account.Name),
            new(AppClaimTypes.DepartmentId, user.OrgId ?? string.Empty),
            new(AppClaimTypes.DepartmentIsCenter, user.Organization?.IsCenter.ToString() ?? false.ToString()),
            new(AppClaimTypes.DepartmentName, user.Organization?.Name ?? string.Empty),
            new(AppClaimTypes.DepartmentAreaCode, user.Organization?.AreaCode ?? string.Empty),
            new(AppClaimTypes.DepartmentAreaName, user.Organization?.AreaName ?? string.Empty),
            new(AppClaimTypes.DepartmentLevel, user.Organization?.Level.ToString() ?? string.Empty),
            new(AppClaimTypes.AreaId, user.OrgId?.GetHigherOrgId() ?? string.Empty),
            new(AppClaimTypes.OpenId, thirdAccount?.OpenId ?? string.Empty),
            new("AppScope", appScope)
        ];
        if (appType != null && thirdType != null)
        {
            var claims = new List<Claim>();
            var userId = _thirdAccountDomainFactory.GetClaimAsync(new ThirdAccount { Id = thirdAccount?.Id, PhoneNumber = phoneNo, AccountType = thirdType.Value, AppType = appType.Value, ExternalId = thirdAccount?.ExternalId }, claims, CancellationToken.None).GetAwaiter().GetResult();
            userClaims.Add(new(ClaimTypes.NameIdentifier, userId));
        }
        else
        {
            userClaims.Add(new(ClaimTypes.NameIdentifier, user.Id));
        }
        userClaims.AddRange(account.Roles.Select(d => new Claim(JwtClaimTypes.Role, d.Name)));
        ClaimsIdentity identity = new ClaimsIdentity(userClaims);
        var principal = new ClaimsPrincipal(identity);
        _httpContextAccessor.HttpContext.User = principal;

    }
}
