using MediatR;
using TakeoutSaaS.Application.Identity.Contracts;
using TakeoutSaaS.Application.Identity.Queries;
using TakeoutSaaS.Domain.Identity.Entities;
using TakeoutSaaS.Domain.Identity.Enums;
using TakeoutSaaS.Domain.Identity.Repositories;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
using TakeoutSaaS.Shared.Abstractions.Results;
using TakeoutSaaS.Shared.Abstractions.Tenancy;

namespace TakeoutSaaS.Application.Identity.Handlers;

/// <summary>
/// 用户分页查询处理器。
/// </summary>
public sealed class SearchIdentityUsersQueryHandler(
    IIdentityUserRepository identityUserRepository,
    IUserRoleRepository userRoleRepository,
    IRoleRepository roleRepository,
    ITenantProvider tenantProvider)
    : IRequestHandler<SearchIdentityUsersQuery, PagedResult<UserListItemDto>>
{
    /// <inheritdoc />
    public async Task<PagedResult<UserListItemDto>> Handle(SearchIdentityUsersQuery request, CancellationToken cancellationToken)
    {
        // 1. 获取当前租户
        var currentTenantId = tenantProvider.GetCurrentTenantId();

        // 2. 校验跨租户访问
        if (request.TenantId.HasValue && request.TenantId.Value != currentTenantId)
        {
            throw new BusinessException(ErrorCodes.Forbidden, "禁止跨租户查询用户");
        }

        // 3. 组装查询过滤条件
        var filter = new IdentityUserSearchFilter
        {
            TenantId = currentTenantId,
            Keyword = request.Keyword,
            Status = request.Status,
            RoleId = request.RoleId,
            CreatedAtFrom = request.CreatedAtFrom,
            CreatedAtTo = request.CreatedAtTo,
            LastLoginFrom = request.LastLoginFrom,
            LastLoginTo = request.LastLoginTo,
            IncludeDeleted = request.IncludeDeleted,
            Page = request.Page,
            PageSize = request.PageSize,
            SortBy = request.SortBy,
            SortDescending = request.SortDescending
        };

        // 4. 执行分页查询
        var (items, total) = await identityUserRepository.SearchPagedAsync(filter, cancellationToken);
        if (items.Count == 0)
        {
            return new PagedResult<UserListItemDto>(Array.Empty<UserListItemDto>(), request.Page, request.PageSize, total);
        }

        // 5. 加载角色编码映射
        var roleCodesLookup = await ResolveRoleCodesAsync(items, userRoleRepository, roleRepository, cancellationToken);

        // 6. 组装 DTO
        var now = DateTime.UtcNow;
        var dtos = items.Select(user => new UserListItemDto
        {
            UserId = user.Id,
            TenantId = user.TenantId,
            Account = user.Account,
            DisplayName = user.DisplayName,
            Avatar = user.Avatar,
            Phone = user.Phone,
            Email = user.Email,
            Status = user.Status,
            IsLocked = IsLocked(user, now),
            IsDeleted = user.DeletedAt.HasValue,
            Roles = roleCodesLookup.TryGetValue(user.Id, out var codes) ? codes : Array.Empty<string>(),
            CreatedAt = user.CreatedAt,
            LastLoginAt = user.LastLoginAt
        }).ToList();

        // 7. 返回分页结果
        return new PagedResult<UserListItemDto>(dtos, request.Page, request.PageSize, total);
    }

    private static bool IsLocked(IdentityUser user, DateTime now)
        => user.Status == IdentityUserStatus.Locked
           || (user.LockedUntil.HasValue && user.LockedUntil.Value > now);

    private static async Task<Dictionary<long, string[]>> ResolveRoleCodesAsync(
        IReadOnlyList<IdentityUser> users,
        IUserRoleRepository userRoleRepository,
        IRoleRepository roleRepository,
        CancellationToken cancellationToken)
    {
        // 1. 预分配字典容量
        var result = new Dictionary<long, string[]>(users.Count);

        // 2. 按租户分组，降低角色查询次数
        foreach (var group in users.GroupBy(user => user.TenantId))
        {
            var tenantId = group.Key;
            var userIds = group.Select(user => user.Id).Distinct().ToArray();
            if (userIds.Length == 0)
            {
                continue;
            }

            // 3. 查询用户角色映射
            var relations = await userRoleRepository.GetByUserIdsAsync(tenantId, userIds, cancellationToken);
            if (relations.Count == 0)
            {
                continue;
            }

            // 4. 查询角色并构建映射
            var roleIds = relations.Select(x => x.RoleId).Distinct().ToArray();
            var roles = roleIds.Length == 0
                ? Array.Empty<Role>()
                : await roleRepository.GetByIdsAsync(tenantId, roleIds, cancellationToken);
            var roleCodeMap = roles.ToDictionary(role => role.Id, role => role.Code, EqualityComparer<long>.Default);

            // 5. 组装用户角色编码列表
            foreach (var relationGroup in relations.GroupBy(x => x.UserId))
            {
                var codes = relationGroup
                    .Select(x => roleCodeMap.TryGetValue(x.RoleId, out var code) ? code : null)
                    .OfType<string>()
                    .Where(code => !string.IsNullOrWhiteSpace(code))
                    .Distinct(StringComparer.OrdinalIgnoreCase)
                    .ToArray();
                result[relationGroup.Key] = codes.Length == 0 ? Array.Empty<string>() : codes;
            }
        }

        return result;
    }
}
