using Microsoft.EntityFrameworkCore;
using Tiku.Application.Commerce;
using Tiku.Infrastructure.Security;

namespace Tiku.Infrastructure.Commerce;

internal sealed partial class CommerceAdminService
{
    public async Task<AdminOrderList> GetOrdersAsync(
        CommerceAdminActor actor,
        CommerceAdminQuery query,
        CancellationToken cancellationToken = default)
    {
        await AssertAdminAsync(actor, cancellationToken);
        var scope = await RequireDataScopeAsync(actor, cancellationToken);
        var regionIds = scope.RegionIds.ToArray();
        var orders = dbContext.Orders.AsNoTracking()
            .Where(item => item.TenantId == actor.TenantId)
            .ApplyDataScope(
                scope,
                item => item.UserId == actor.UserId,
                item => item.RegionId.HasValue && regionIds.Contains(item.RegionId.Value));
        if (!string.IsNullOrWhiteSpace(query.Status))
            orders = orders.Where(item => item.Status == ParseOrderStatus(query.Status));

        var items = await orders
            .OrderByDescending(item => item.CreatedAt)
            .Take(Math.Clamp(query.Limit ?? 50, 1, 200))
            .ToArrayAsync(cancellationToken);
        return new AdminOrderList(items.Select(ToOrderItem).ToArray());
    }

    public async Task<AdminPaymentList> GetPaymentsAsync(
        CommerceAdminActor actor,
        CommerceAdminQuery query,
        CancellationToken cancellationToken = default)
    {
        await AssertAdminAsync(actor, cancellationToken);
        var scope = await RequireDataScopeAsync(actor, cancellationToken);
        var regionIds = scope.RegionIds.ToArray();
        var scopedOrders = dbContext.Orders.AsNoTracking()
            .Where(order => order.TenantId == actor.TenantId)
            .ApplyDataScope(
                scope,
                order => order.UserId == actor.UserId,
                order => order.RegionId.HasValue && regionIds.Contains(order.RegionId.Value));
        var payments = from payment in dbContext.Payments.AsNoTracking()
            join order in scopedOrders
                on new { payment.TenantId, payment.OrderId } equals new { order.TenantId, OrderId = order.Id }
            where payment.TenantId == actor.TenantId
            select new { payment, order.OrderNo };
        if (!string.IsNullOrWhiteSpace(query.Provider))
        {
            var provider = NormalizeProvider(query.Provider);
            payments = payments.Where(item => item.payment.Provider == provider);
        }

        if (!string.IsNullOrWhiteSpace(query.Status))
            payments = payments.Where(item => item.payment.Status == ParsePaymentStatus(query.Status));

        var rows = await payments
            .OrderByDescending(item => item.payment.CreatedAt)
            .Take(Math.Clamp(query.Limit ?? 50, 1, 200))
            .ToArrayAsync(cancellationToken);
        return new AdminPaymentList(rows.Select(item => ToPaymentItem(item.payment, item.OrderNo)).ToArray());
    }
}