using MediatR;
using TakeoutSaaS.Application.App.Merchants.Dto;
using TakeoutSaaS.Application.App.Merchants.Queries;
using TakeoutSaaS.Domain.Merchants.Repositories;
using TakeoutSaaS.Domain.Stores.Repositories;
using TakeoutSaaS.Domain.Tenants.Repositories;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
using TakeoutSaaS.Shared.Abstractions.Results;
using TakeoutSaaS.Shared.Abstractions.Tenancy;

namespace TakeoutSaaS.Application.App.Merchants.Handlers;

/// <summary>
/// 商户列表查询处理器。
/// </summary>
public sealed class GetMerchantListQueryHandler(
    IMerchantRepository merchantRepository,
    IStoreRepository storeRepository,
    ITenantRepository tenantRepository,
    ITenantProvider tenantProvider)
    : IRequestHandler<GetMerchantListQuery, PagedResult<MerchantListItemDto>>
{
    /// <inheritdoc />
    public async Task<PagedResult<MerchantListItemDto>> Handle(
        GetMerchantListQuery request,
        CancellationToken cancellationToken)
    {
        // 1. 获取当前租户并校验跨租户访问
        var currentTenantId = tenantProvider.GetCurrentTenantId();
        if (request.TenantId.HasValue && request.TenantId.Value != currentTenantId)
        {
            throw new BusinessException(ErrorCodes.Forbidden, "禁止跨租户查询商户");
        }

        var effectiveTenantId = currentTenantId;

        // 2. 查询商户列表
        var merchants = await merchantRepository.SearchAsync(
            effectiveTenantId,
            request.Status,
            request.OperatingMode,
            request.Keyword,
            cancellationToken);

        if (merchants.Count == 0)
        {
            return new PagedResult<MerchantListItemDto>(Array.Empty<MerchantListItemDto>(), request.Page, request.PageSize, 0);
        }

        // 3. 排序 & 分页
        var sorted = ApplySorting(merchants, request.SortBy, request.SortOrder);
        var total = sorted.Count;
        var paged = sorted
            .Skip((request.Page - 1) * request.PageSize)
            .Take(request.PageSize)
            .ToList();

        if (paged.Count == 0)
        {
            return new PagedResult<MerchantListItemDto>(Array.Empty<MerchantListItemDto>(), request.Page, request.PageSize, total);
        }

        // 4. 批量查询租户名称
        var tenantIds = paged.Select(x => x.TenantId).Distinct().ToArray();
        var tenants = await tenantRepository.FindByIdsAsync(tenantIds, cancellationToken);
        var tenantLookup = tenants.ToDictionary(x => x.Id, x => x.Name);

        // 5. 批量查询门店数量
        var merchantIds = paged.Select(x => x.Id).ToArray();
        var storeCounts = await storeRepository.GetStoreCountsAsync(effectiveTenantId, merchantIds, cancellationToken);

        // 6. 组装 DTO
        var items = paged.Select(merchant =>
        {
            var tenantName = tenantLookup.TryGetValue(merchant.TenantId, out var name) ? name : null;
            var count = storeCounts.TryGetValue(merchant.Id, out var value) ? value : 0;
            return MerchantMapping.ToListItemDto(merchant, tenantName, count);
        }).ToList();

        return new PagedResult<MerchantListItemDto>(items, request.Page, request.PageSize, total);
    }

    private static List<Domain.Merchants.Entities.Merchant> ApplySorting(
        IReadOnlyList<Domain.Merchants.Entities.Merchant> merchants,
        string? sortBy,
        string? sortOrder)
    {
        var descending = !string.Equals(sortOrder, "asc", StringComparison.OrdinalIgnoreCase);
        return (sortBy ?? string.Empty).Trim().ToLowerInvariant() switch
        {
            "name" => descending ? merchants.OrderByDescending(x => x.BrandName).ToList() : merchants.OrderBy(x => x.BrandName).ToList(),
            "status" => descending ? merchants.OrderByDescending(x => x.Status).ToList() : merchants.OrderBy(x => x.Status).ToList(),
            "updatedat" => descending ? merchants.OrderByDescending(x => x.UpdatedAt ?? x.CreatedAt).ToList() : merchants.OrderBy(x => x.UpdatedAt ?? x.CreatedAt).ToList(),
            _ => descending ? merchants.OrderByDescending(x => x.CreatedAt).ToList() : merchants.OrderBy(x => x.CreatedAt).ToList()
        };
    }
}
