namespace WalletService.API.Application.Queries;

using MediatR;
using Microsoft.EntityFrameworkCore;
using WalletService.Domain.AggregatesModel.PointAccountAggregate;
using WalletService.Infrastructure;

/// <summary>
/// EN: Handler for GetAllPointAccountsQuery (Admin).
/// VI: Handler cho GetAllPointAccountsQuery (Admin).
/// </summary>
public class GetAllPointAccountsQueryHandler : IRequestHandler<GetAllPointAccountsQuery, AdminPointAccountsListDto>
{
    private readonly WalletServiceContext _context;

    public GetAllPointAccountsQueryHandler(WalletServiceContext context)
    {
        _context = context;
    }

    public async Task<AdminPointAccountsListDto> Handle(
        GetAllPointAccountsQuery request, 
        CancellationToken cancellationToken)
    {
        var query = _context.PointAccounts.AsQueryable();

        // EN: Filter by points range if provided
        // VI: Lọc theo khoảng điểm nếu có
        if (request.MinPoints.HasValue)
            query = query.Where(p => p.AvailablePoints >= request.MinPoints.Value);

        if (request.MaxPoints.HasValue)
            query = query.Where(p => p.AvailablePoints <= request.MaxPoints.Value);

        var totalCount = await query.CountAsync(cancellationToken);

        var accounts = await query
            .OrderByDescending(p => p.TotalPoints)
            .Skip((request.Page - 1) * request.PageSize)
            .Take(request.PageSize)
            .Select(p => new AdminPointAccountSummaryDto(
                p.Id,
                p.UserId,
                p.TotalPoints,
                p.AvailablePoints,
                p.CreatedAt))
            .ToListAsync(cancellationToken);

        return new AdminPointAccountsListDto(accounts, totalCount, request.Page, request.PageSize);
    }
}

/// <summary>
/// EN: Handler for GetPointAccountByIdQuery (Admin).
/// VI: Handler cho GetPointAccountByIdQuery (Admin).
/// </summary>
public class GetPointAccountByIdQueryHandler : IRequestHandler<GetPointAccountByIdQuery, AdminPointAccountDetailDto?>
{
    private readonly WalletServiceContext _context;

    public GetPointAccountByIdQueryHandler(WalletServiceContext context)
    {
        _context = context;
    }

    public async Task<AdminPointAccountDetailDto?> Handle(
        GetPointAccountByIdQuery request, 
        CancellationToken cancellationToken)
    {
        var account = await _context.PointAccounts
            .Include(p => p.Transactions)
            .FirstOrDefaultAsync(p => p.Id == request.AccountId, cancellationToken);

        if (account == null) return null;

        return new AdminPointAccountDetailDto(
            account.Id,
            account.UserId,
            account.TotalPoints,
            account.AvailablePoints,
            account.Transactions.Count,
            account.CreatedAt,
            account.UpdatedAt);
    }
}

/// <summary>
/// EN: Handler for SearchPointAccountsQuery (Admin).
/// VI: Handler cho SearchPointAccountsQuery (Admin).
/// </summary>
public class SearchPointAccountsQueryHandler : IRequestHandler<SearchPointAccountsQuery, List<AdminPointAccountDetailDto>>
{
    private readonly WalletServiceContext _context;

    public SearchPointAccountsQueryHandler(WalletServiceContext context)
    {
        _context = context;
    }

    public async Task<List<AdminPointAccountDetailDto>> Handle(
        SearchPointAccountsQuery request, 
        CancellationToken cancellationToken)
    {
        var query = _context.PointAccounts
            .Include(p => p.Transactions)
            .AsQueryable();

        if (request.UserId.HasValue)
            query = query.Where(p => p.UserId == request.UserId.Value);

        var accounts = await query
            .Take(50)
            .Select(p => new AdminPointAccountDetailDto(
                p.Id,
                p.UserId,
                p.TotalPoints,
                p.AvailablePoints,
                p.Transactions.Count,
                p.CreatedAt,
                p.UpdatedAt))
            .ToListAsync(cancellationToken);

        return accounts;
    }
}

/// <summary>
/// EN: Handler for GetPointsStatisticsQuery (Admin).
/// VI: Handler cho GetPointsStatisticsQuery (Admin).
/// </summary>
public class GetPointsStatisticsQueryHandler : IRequestHandler<GetPointsStatisticsQuery, PointsStatisticsDto>
{
    private readonly WalletServiceContext _context;

    public GetPointsStatisticsQueryHandler(WalletServiceContext context)
    {
        _context = context;
    }

    public async Task<PointsStatisticsDto> Handle(
        GetPointsStatisticsQuery request, 
        CancellationToken cancellationToken)
    {
        var today = DateTime.UtcNow.Date;

        var totalAccounts = await _context.PointAccounts.CountAsync(cancellationToken);

        var totalPointsIssued = await _context.PointAccounts
            .SumAsync(p => p.TotalPoints, cancellationToken);

        var totalPointsAvailable = await _context.PointAccounts
            .SumAsync(p => p.AvailablePoints, cancellationToken);

        // EN: Calculate spent and expired from transactions
        // VI: Tính điểm đã tiêu và hết hạn từ giao dịch
        var totalPointsSpent = await _context.PointTransactions
            .Where(t => t.TypeId == PointTransactionType.Spend.Id)
            .SumAsync(t => t.Points, cancellationToken);

        var totalPointsExpired = await _context.PointTransactions
            .Where(t => t.TypeId == PointTransactionType.Expire.Id)
            .SumAsync(t => t.Points, cancellationToken);

        var pointsEarnedToday = await _context.PointTransactions
            .Where(t => t.CreatedAt >= today && t.TypeId == PointTransactionType.Earn.Id)
            .SumAsync(t => t.Points, cancellationToken);

        var pointsSpentToday = await _context.PointTransactions
            .Where(t => t.CreatedAt >= today && t.TypeId == PointTransactionType.Spend.Id)
            .SumAsync(t => t.Points, cancellationToken);

        return new PointsStatisticsDto(
            totalAccounts,
            totalPointsIssued,
            totalPointsAvailable,
            totalPointsSpent,
            totalPointsExpired,
            pointsEarnedToday,
            pointsSpentToday);
    }
}
