using Hotline.Configurations;
using Hotline.Users;
using IdentityModel;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using System.Security.Claims;
using Hotline.Settings;
using XF.Domain.Authentications;
using XF.Domain.Dependency;
using XF.Domain.Repository;
using Hotline.Identity.Accounts;
using Hotline.Share.Dtos.FlowEngine;
using Hotline.Share.Dtos.FlowEngine.Workflow;

namespace Hotline.Authentications;

public class SessionContextProvider : ISessionContextProvider, IScopeDependency
{
    private readonly IServiceProvider _serviceProvider;

    public SessionContextProvider(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public HttpContext ChangeSessionByUserId(string id, HttpContext httpContext)
    {
        if (httpContext == null)
        {
            httpContext = new DefaultHttpContext();
        }

        var userRepository = _serviceProvider.GetService<IRepository<User>>();
        var accountRepository = _serviceProvider.GetService<IAccountRepository>();
        var user = userRepository.Queryable().Where(m => m.Id == id).First();
        if (user == null) return null;
        var account = accountRepository.GetExtAsync(m => m.Id == user.Id, m => m.Includes(x => x.Roles)).GetAwaiter().GetResult();

        List<Claim> userClaims =
        [
            new(JwtClaimTypes.Subject, account.Id),
            new(JwtClaimTypes.PhoneNumber, account.PhoneNo ?? string.Empty),
            new(ClaimTypes.NameIdentifier, user.Id),
            new(AppClaimTypes.UserDisplayName, account.Name),
            new(AppClaimTypes.DepartmentId, user.OrgId ?? string.Empty),
            new(AppClaimTypes.DepartmentIsCenter, user.Organization?.IsCenter.ToString() ?? string.Empty),
            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),
        ];
        userClaims.AddRange(account.Roles.Select(d => new Claim(JwtClaimTypes.Role, d.Name)));
        httpContext.User = new ClaimsPrincipal(new ClaimsIdentity(userClaims));
        return httpContext;
    }

    public async Task ChangeSessionByUserIdAsync(string userId, CancellationToken cancellation)
    {
        var httpContext = _serviceProvider.GetRequiredService<IHttpContextAccessor>().HttpContext;
        var accountRepository = _serviceProvider.GetService<IRepository<Account>>();
        httpContext ??= new DefaultHttpContext();

        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)));
        httpContext.User = new ClaimsPrincipal(new ClaimsIdentity(userClaims));
    }

    public void ChangeSession(string userId, string username, string orgId, string orgname, int orgLevel, string orgAreaCode, string orgAreaName, string[] roles)
    {
        var httpContext = _serviceProvider.GetRequiredService<IHttpContextAccessor>().HttpContext;
        httpContext ??= new DefaultHttpContext();

        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.DepartmentAreaCode, orgAreaCode ?? string.Empty),
            new(AppClaimTypes.DepartmentAreaName, orgAreaName ?? string.Empty),
            new(AppClaimTypes.DepartmentLevel, orgLevel.ToString() ?? string.Empty),
            new(AppClaimTypes.AreaId, orgId?.GetHigherOrgId() ?? string.Empty),
        ];
        userClaims.AddRange(roles.Select(d => new Claim(JwtClaimTypes.Role, d)));
        httpContext.User = new ClaimsPrincipal(new ClaimsIdentity(userClaims));
    }

    public void ChangeSession(ISessionContext sessionContext) =>
        ChangeSession(
            sessionContext.UserId,
            sessionContext.UserName,
            sessionContext.OrgId,
            sessionContext.OrgName,
            sessionContext.OrgLevel,
            sessionContext.OrgAreaCode,
            sessionContext.OrgAreaName,
            sessionContext.Roles
        );
}