using System.Text.Json;
using AdsServingService.Infrastructure;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace AdsServingService.API.Application.Queries;

/// <summary>
/// EN: Handler for GetAuctionStatisticsQuery.
/// VI: Handler cho GetAuctionStatisticsQuery.
/// </summary>
public class GetAuctionStatisticsQueryHandler
    : IRequestHandler<GetAuctionStatisticsQuery, AuctionStatisticsDto>
{
    private readonly AdsServingServiceContext _context;

    public GetAuctionStatisticsQueryHandler(AdsServingServiceContext context)
    {
        _context = context ?? throw new ArgumentNullException(nameof(context));
    }

    public async Task<AuctionStatisticsDto> Handle(
        GetAuctionStatisticsQuery request,
        CancellationToken cancellationToken)
    {
        var auctionSnapshots = await _context.Auctions
            .AsNoTracking()
            .Select(auction => new
            {
                HasWinner = auction.Result != null,
                WinningEcpm = auction.Result != null ? auction.Result.WinningeCPM : 0m,
                BidsJson = EF.Property<string>(auction, "_bidsJson"),
            })
            .ToListAsync(cancellationToken);

        var totalAuctions = auctionSnapshots.Count;
        var totalWinners = auctionSnapshots.Count(snapshot => snapshot.HasWinner);
        var averageWinRate = totalAuctions == 0
            ? 0m
            : Math.Round((decimal)totalWinners / totalAuctions * 100m, 2);

        var winningEcpmValues = auctionSnapshots
            .Where(snapshot => snapshot.HasWinner)
            .Select(snapshot => snapshot.WinningEcpm)
            .ToList();

        var averageEcpm = winningEcpmValues.Count == 0
            ? 0m
            : Math.Round(winningEcpmValues.Average(), 4);

        var totalBidsPlaced = auctionSnapshots
            .Sum(snapshot => CountBids(snapshot.BidsJson));

        return new AuctionStatisticsDto
        {
            TotalAuctions = totalAuctions,
            AverageWinRate = averageWinRate,
            AverageeCPM = averageEcpm,
            TotalBidsPlaced = totalBidsPlaced,
        };
    }

    private static int CountBids(string? bidsJson)
    {
        if (string.IsNullOrWhiteSpace(bidsJson))
        {
            return 0;
        }

        try
        {
            using var document = JsonDocument.Parse(bidsJson);
            return document.RootElement.ValueKind == JsonValueKind.Array
                ? document.RootElement.GetArrayLength()
                : 0;
        }
        catch (JsonException)
        {
            return 0;
        }
    }
}
