// EN: Query for staff performance analytics — orders handled, revenue, completion rates.
// VI: Query cho phan tich hieu suat nhan vien — don xu ly, doanh thu, ty le hoan thanh.

using System.Data;
using Dapper;
using MediatR;

namespace OrderService.API.Application.Queries.Reports;

/// <summary>
/// EN: Query for staff performance metrics over a date range.
/// VI: Query cho chi so hieu suat nhan vien trong khoang thoi gian.
/// </summary>
public record GetStaffPerformanceQuery(
    Guid ShopId,
    DateTime StartDate,
    DateTime EndDate
) : IRequest<StaffPerformanceDto>;

/// <summary>
/// EN: Staff performance report with individual metrics and shop average.
/// VI: Bao cao hieu suat nhan vien voi chi so ca nhan va trung binh cua hang.
/// </summary>
public record StaffPerformanceDto(
    List<StaffMetricsDto> Staff,
    StaffMetricsDto ShopAverage
);

/// <summary>
/// EN: Individual staff member metrics (class for Dapper compatibility).
/// VI: Chi so nhan vien ca nhan (class cho tuong thich Dapper).
/// </summary>
public class StaffMetricsDto
{
    public Guid? StaffId { get; set; }
    public string StaffName { get; set; } = string.Empty;
    public int OrdersHandled { get; set; }
    public decimal TotalRevenue { get; set; }
    public decimal AverageOrderValue { get; set; }
    public int CompletedOrders { get; set; }
    public int CancelledOrders { get; set; }
    public double CompletionRate { get; set; }
    public double AverageHandlingTimeMinutes { get; set; }
}

/// <summary>
/// EN: Handler for GetStaffPerformanceQuery — aggregates staff order data using Dapper.
/// VI: Handler cho GetStaffPerformanceQuery — tong hop du lieu don hang nhan vien bang Dapper.
/// </summary>
public class GetStaffPerformanceQueryHandler : IRequestHandler<GetStaffPerformanceQuery, StaffPerformanceDto>
{
    private readonly IDbConnection _connection;
    private readonly ILogger<GetStaffPerformanceQueryHandler> _logger;

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

    public async Task<StaffPerformanceDto> Handle(GetStaffPerformanceQuery request, CancellationToken cancellationToken)
    {
        _logger.LogInformation(
            "EN: Generating staff performance report for shop {ShopId} from {Start} to {End} / VI: Tao bao cao hieu suat nhan vien cho shop {ShopId} tu {Start} den {End}",
            request.ShopId, request.StartDate.ToString("yyyy-MM-dd"), request.EndDate.ToString("yyyy-MM-dd"));

        var startDate = request.StartDate.Date;
        var endDate = request.EndDate.Date.AddDays(1);

        var parameters = new DynamicParameters();
        parameters.Add("ShopId", request.ShopId);
        parameters.Add("StartDate", startDate);
        parameters.Add("EndDate", endDate);
        parameters.Add("CompletedStatusId", 5);
        parameters.Add("CancelledStatusId", 6);

        // EN: Staff performance metrics grouped by staff member
        // VI: Chi so hieu suat nhan vien nhom theo nhan vien
        var staffSql = @"
            SELECT
                o.staff_id AS StaffId,
                COALESCE(o.staff_name, 'Chua gan') AS StaffName,
                COUNT(*)::int AS OrdersHandled,
                COALESCE(SUM(o.total_amount) FILTER (WHERE o.status_id != @CancelledStatusId), 0) AS TotalRevenue,
                COALESCE(AVG(o.total_amount) FILTER (WHERE o.status_id != @CancelledStatusId), 0) AS AverageOrderValue,
                COUNT(*) FILTER (WHERE o.status_id = @CompletedStatusId)::int AS CompletedOrders,
                COUNT(*) FILTER (WHERE o.status_id = @CancelledStatusId)::int AS CancelledOrders,
                CASE
                    WHEN COUNT(*) > 0
                    THEN ROUND(COUNT(*) FILTER (WHERE o.status_id = @CompletedStatusId)::numeric / COUNT(*)::numeric * 100, 2)
                    ELSE 0
                END AS CompletionRate,
                COALESCE(
                    AVG(EXTRACT(EPOCH FROM (o.completed_at - o.created_at)) / 60.0)
                    FILTER (WHERE o.completed_at IS NOT NULL AND o.status_id = @CompletedStatusId),
                    0
                ) AS AverageHandlingTimeMinutes
            FROM orders o
            WHERE o.shop_id = @ShopId
              AND o.created_at >= @StartDate
              AND o.created_at < @EndDate
            GROUP BY o.staff_id, o.staff_name
            ORDER BY TotalRevenue DESC";

        var staffMetrics = (await _connection.QueryAsync<StaffMetricsDto>(staffSql, parameters, commandTimeout: 5)).AsList();

        // EN: Calculate shop average / VI: Tinh trung binh cua hang
        var shopAverage = new StaffMetricsDto
        {
            StaffId = null,
            StaffName = "Trung binh cua hang",
            OrdersHandled = staffMetrics.Any() ? (int)Math.Round(staffMetrics.Average(s => s.OrdersHandled)) : 0,
            TotalRevenue = staffMetrics.Any() ? Math.Round(staffMetrics.Average(s => s.TotalRevenue), 0) : 0,
            AverageOrderValue = staffMetrics.Any() ? Math.Round(staffMetrics.Average(s => s.AverageOrderValue), 0) : 0,
            CompletedOrders = staffMetrics.Any() ? (int)Math.Round(staffMetrics.Average(s => s.CompletedOrders)) : 0,
            CancelledOrders = staffMetrics.Any() ? (int)Math.Round(staffMetrics.Average(s => s.CancelledOrders)) : 0,
            CompletionRate = staffMetrics.Any() ? Math.Round(staffMetrics.Average(s => s.CompletionRate), 2) : 0,
            AverageHandlingTimeMinutes = staffMetrics.Any() ? Math.Round(staffMetrics.Average(s => s.AverageHandlingTimeMinutes), 1) : 0
        };

        return new StaffPerformanceDto(staffMetrics, shopAverage);
    }
}
