// EN: Query for End-of-Day report data.
// VI: Query lay du lieu bao cao cuoi ngay.

using System.Data;
using Dapper;
using MediatR;

namespace OrderService.API.Application.Queries.Reports;

/// <summary>
/// EN: Query for End-of-Day report aggregating orders, revenue, payments, and top items.
/// VI: Query cho bao cao cuoi ngay tong hop don hang, doanh thu, thanh toan, va san pham ban chay.
/// </summary>
public record GetEodReportQuery(
    Guid ShopId,
    DateTime ReportDate
) : IRequest<EodReportDto>;

/// <summary>
/// EN: End-of-Day report DTO with complete daily summary.
/// VI: DTO bao cao cuoi ngay voi tom tat day du trong ngay.
/// </summary>
public record EodReportDto(
    DateTime ReportDate,
    Guid ShopId,
    int TotalOrders,
    int CompletedOrders,
    int CancelledOrders,
    decimal TotalRevenue,
    decimal CashRevenue,
    decimal CardRevenue,
    decimal OnlineRevenue,
    decimal DiscountTotal,
    List<EodPaymentBreakdownDto> PaymentBreakdown,
    List<EodTopItemDto> TopItems,
    List<EodHourlyRevenueDto> HourlyRevenue
);

/// <summary>
/// EN: Payment breakdown by method (class for Dapper compatibility).
/// VI: Phan tich thanh toan theo phuong thuc (class cho tuong thich Dapper).
/// </summary>
public class EodPaymentBreakdownDto
{
    public string Method { get; set; } = string.Empty;
    public int Count { get; set; }
    public decimal Amount { get; set; }
}

/// <summary>
/// EN: Top selling item for the day (class for Dapper compatibility).
/// VI: San pham ban chay trong ngay (class cho tuong thich Dapper).
/// </summary>
public class EodTopItemDto
{
    public string ItemName { get; set; } = string.Empty;
    public int Quantity { get; set; }
    public decimal Revenue { get; set; }
}

/// <summary>
/// EN: Revenue data per hour (class for Dapper compatibility).
/// VI: Du lieu doanh thu theo gio (class cho tuong thich Dapper).
/// </summary>
public class EodHourlyRevenueDto
{
    public int Hour { get; set; }
    public int OrderCount { get; set; }
    public decimal Revenue { get; set; }
}

/// <summary>
/// EN: Handler for GetEodReportQuery — aggregates daily order data using Dapper.
/// VI: Handler cho GetEodReportQuery — tong hop du lieu don hang hang ngay bang Dapper.
/// </summary>
public class GetEodReportQueryHandler : IRequestHandler<GetEodReportQuery, EodReportDto>
{
    private readonly IDbConnection _connection;
    private readonly ILogger<GetEodReportQueryHandler> _logger;

    public GetEodReportQueryHandler(IDbConnection connection, ILogger<GetEodReportQueryHandler> logger)
    {
        _connection = connection ?? throw new ArgumentNullException(nameof(connection));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    public async Task<EodReportDto> Handle(GetEodReportQuery request, CancellationToken cancellationToken)
    {
        _logger.LogInformation(
            "EN: Generating EOD report for shop {ShopId} on {Date} / VI: Tao bao cao cuoi ngay cho shop {ShopId} ngay {Date}",
            request.ShopId, request.ReportDate.ToString("yyyy-MM-dd"));

        var dayStart = request.ReportDate.Date;
        var dayEnd = dayStart.AddDays(1);

        var parameters = new DynamicParameters();
        parameters.Add("ShopId", request.ShopId);
        parameters.Add("DayStart", dayStart);
        parameters.Add("DayEnd", dayEnd);

        // EN: Completed status ID = 5, Cancelled status ID = 6 (from OrderStatus enumeration)
        // VI: ID trang thai hoan thanh = 5, ID trang thai huy = 6 (tu OrderStatus enumeration)
        parameters.Add("CompletedStatusId", 5);
        parameters.Add("CancelledStatusId", 6);

        // EN: Aggregate order stats for the day / VI: Thong ke tong hop don hang trong ngay
        var aggregateSql = @"
            SELECT
                COUNT(*) AS TotalOrders,
                COUNT(*) FILTER (WHERE o.status_id = @CompletedStatusId) AS CompletedOrders,
                COUNT(*) FILTER (WHERE o.status_id = @CancelledStatusId) AS CancelledOrders,
                COALESCE(SUM(o.total_amount) FILTER (WHERE o.status_id NOT IN (@CancelledStatusId)), 0) AS TotalRevenue,
                COALESCE(SUM(o.total_amount) FILTER (WHERE o.payment_method = 'cash' AND o.status_id NOT IN (@CancelledStatusId)), 0) AS CashRevenue,
                COALESCE(SUM(o.total_amount) FILTER (WHERE o.payment_method = 'card' AND o.status_id NOT IN (@CancelledStatusId)), 0) AS CardRevenue,
                COALESCE(SUM(o.total_amount) FILTER (WHERE o.payment_method NOT IN ('cash', 'card') AND o.payment_method IS NOT NULL AND o.status_id NOT IN (@CancelledStatusId)), 0) AS OnlineRevenue,
                COALESCE(SUM(o.discount_amount) FILTER (WHERE o.status_id NOT IN (@CancelledStatusId)), 0) AS DiscountTotal
            FROM orders o
            WHERE o.shop_id = @ShopId
              AND o.created_at >= @DayStart
              AND o.created_at < @DayEnd";

        var aggregate = await _connection.QuerySingleAsync<AggregateRow>(aggregateSql, parameters);

        // EN: Payment breakdown by method / VI: Phan tich theo phuong thuc thanh toan
        var paymentSql = @"
            SELECT
                COALESCE(o.payment_method, 'unknown') AS Method,
                COUNT(*) AS Count,
                COALESCE(SUM(o.total_amount), 0) AS Amount
            FROM orders o
            WHERE o.shop_id = @ShopId
              AND o.created_at >= @DayStart
              AND o.created_at < @DayEnd
              AND o.status_id NOT IN (@CancelledStatusId)
            GROUP BY o.payment_method
            ORDER BY Amount DESC";

        var paymentBreakdown = (await _connection.QueryAsync<EodPaymentBreakdownDto>(paymentSql, parameters, commandTimeout: 5)).AsList();

        // EN: Top 10 items by quantity sold / VI: Top 10 san pham theo so luong ban
        var topItemsSql = @"
            SELECT
                oi.product_name AS ItemName,
                SUM(oi.quantity) AS Quantity,
                SUM(oi.unit_price * oi.quantity) AS Revenue
            FROM order_items oi
            INNER JOIN orders o ON o.id = oi.order_id
            WHERE o.shop_id = @ShopId
              AND o.created_at >= @DayStart
              AND o.created_at < @DayEnd
              AND o.status_id NOT IN (@CancelledStatusId)
            GROUP BY oi.product_name
            ORDER BY Quantity DESC
            LIMIT 10";

        var topItems = (await _connection.QueryAsync<EodTopItemDto>(topItemsSql, parameters, commandTimeout: 5)).AsList();

        // EN: Hourly revenue breakdown / VI: Doanh thu theo tung gio
        var hourlySql = @"
            SELECT
                EXTRACT(HOUR FROM o.created_at)::int AS Hour,
                COUNT(*) AS OrderCount,
                COALESCE(SUM(o.total_amount), 0) AS Revenue
            FROM orders o
            WHERE o.shop_id = @ShopId
              AND o.created_at >= @DayStart
              AND o.created_at < @DayEnd
              AND o.status_id NOT IN (@CancelledStatusId)
            GROUP BY EXTRACT(HOUR FROM o.created_at)
            ORDER BY Hour";

        var hourlyRevenue = (await _connection.QueryAsync<EodHourlyRevenueDto>(hourlySql, parameters, commandTimeout: 5)).AsList();

        return new EodReportDto(
            request.ReportDate.Date,
            request.ShopId,
            (int)aggregate.TotalOrders,
            (int)aggregate.CompletedOrders,
            (int)aggregate.CancelledOrders,
            aggregate.TotalRevenue,
            aggregate.CashRevenue,
            aggregate.CardRevenue,
            aggregate.OnlineRevenue,
            aggregate.DiscountTotal,
            paymentBreakdown,
            topItems,
            hourlyRevenue);
    }

    private record AggregateRow
    {
        public long TotalOrders { get; init; }
        public long CompletedOrders { get; init; }
        public long CancelledOrders { get; init; }
        public decimal TotalRevenue { get; init; }
        public decimal CashRevenue { get; init; }
        public decimal CardRevenue { get; init; }
        public decimal OnlineRevenue { get; init; }
        public decimal DiscountTotal { get; init; }
    }
}
