// EN: Handler for CreateOrderCommand.
// VI: Handler cho CreateOrderCommand.

using MediatR;
using OrderService.API.Hubs;
using OrderService.Domain.AggregatesModel.OrderAggregate;
using OrderService.Domain.Strategies;

namespace OrderService.API.Application.Commands;

/// <summary>
/// EN: Handler for creating a new order with strategy-based validation.
/// VI: Handler tạo order mới với validation qua strategy.
/// </summary>
public class CreateOrderCommandHandler : IRequestHandler<CreateOrderCommand, CreateOrderResult>
{
    private readonly IOrderRepository _orderRepository;
    private readonly IEnumerable<ILineItemStrategy> _strategies;
    private readonly IPosNotificationService _posNotificationService;
    private readonly ILogger<CreateOrderCommandHandler> _logger;

    public CreateOrderCommandHandler(
        IOrderRepository orderRepository,
        IEnumerable<ILineItemStrategy> strategies,
        IPosNotificationService posNotificationService,
        ILogger<CreateOrderCommandHandler> logger)
    {
        _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
        _strategies = strategies ?? throw new ArgumentNullException(nameof(strategies));
        _posNotificationService = posNotificationService ?? throw new ArgumentNullException(nameof(posNotificationService));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    public async Task<CreateOrderResult> Handle(
        CreateOrderCommand request,
        CancellationToken cancellationToken)
    {
        _logger.LogInformation(
            "EN: Creating order for shop {ShopId} / VI: Tạo order cho shop {ShopId}",
            request.ShopId);

        // EN: Create order aggregate
        // VI: Tạo order aggregate
        var order = new Order(request.ShopId, request.CustomerId, request.TableId);

        // EN: Add items to order
        // VI: Thêm items vào order
        foreach (var itemRequest in request.Items)
        {
            var orderItem = new OrderItem(
                itemRequest.ProductId,
                itemRequest.ProductName,
                itemRequest.ProductType,
                itemRequest.Quantity,
                itemRequest.UnitPrice,
                trackInventory: itemRequest.TrackInventory);

            order.AddItem(orderItem);
        }

        // EN: Validate all items through their strategies
        // VI: Validate tất cả items qua strategies của chúng
        foreach (var item in order.Items)
        {
            var strategy = GetStrategy(item.ProductType);
            var isValid = await strategy.ValidateAsync(item, request.ShopId, cancellationToken);

            if (!isValid)
            {
                throw new InvalidOperationException(
                    $"Validation failed for item {item.ProductName} (type: {item.ProductType})");
            }
        }

        // EN: Execute all items via their strategies (e.g., create kitchen tickets for PreparedFood)
        // VI: Thực thi tất cả items qua strategies (vd: tạo phiếu bếp cho PreparedFood)
        foreach (var item in order.Items)
        {
            var strategy = GetStrategy(item.ProductType);
            await strategy.ExecuteAsync(item, request.ShopId, cancellationToken);
        }

        // EN: Apply discount if provided
        // VI: Áp dụng giảm giá nếu có
        if (request.DiscountAmount is > 0)
        {
            order.ApplyDiscount(request.DiscountAmount.Value, request.DiscountType, request.DiscountReference);
        }

        // EN: Mark order as validated after all items pass validation
        // VI: Đánh dấu order là validated sau khi tất cả items pass validation
        order.MarkAsValidated();

        // EN: Save order
        // VI: Lưu order
        _orderRepository.Add(order);
        await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);

        _logger.LogInformation(
            "EN: Order created successfully / VI: Tạo order thành công: {OrderId}",
            order.Id);

        // EN: Send real-time notification to POS/KDS clients
        // VI: Gửi thông báo real-time đến POS/KDS clients
        try
        {
            var orderNotification = new OrderNotificationDto(
                OrderId: order.Id,
                ShopId: order.ShopId,
                Status: order.Status.Name,
                Items: order.Items.Select(i => new OrderItemNotificationDto(
                    ItemId: i.Id,
                    ProductId: i.ProductId,
                    ProductName: i.ProductName,
                    ProductType: i.ProductType,
                    Quantity: i.Quantity,
                    UnitPrice: i.UnitPrice,
                    TotalPrice: i.TotalPrice,
                    Status: i.Status
                )).ToList().AsReadOnly(),
                TotalAmount: order.TotalAmount,
                CustomerId: order.CustomerId,
                TableId: order.TableId,
                CreatedAt: order.CreatedAt,
                UpdatedAt: order.UpdatedAt
            );

            await _posNotificationService.NotifyOrderCreatedAsync(
                order.ShopId, orderNotification, cancellationToken);
        }
        catch (Exception ex)
        {
            // EN: Don't fail the command if notification fails
            // VI: Không fail command nếu notification thất bại
            _logger.LogWarning(ex,
                "EN: Failed to send POS notification for order {OrderId} / " +
                "VI: Gửi thông báo POS thất bại cho order {OrderId}",
                order.Id);
        }

        return new CreateOrderResult(
            order.Id,
            order.TotalAmount,
            order.Status.Name);
    }

    private ILineItemStrategy GetStrategy(string productType)
    {
        var strategy = _strategies.FirstOrDefault(s => s.SupportedType == productType);
        if (strategy == null)
        {
            throw new InvalidOperationException(
                $"EN: No strategy found for product type / VI: Không tìm thấy strategy cho loại sản phẩm: {productType}");
        }
        return strategy;
    }
}
