// EN: Domain event handler for KitchenTicketServedDomainEvent.
//     Looks up the recipe for the served product and calls inventory-service to deduct ingredients.
// VI: Domain event handler cho KitchenTicketServedDomainEvent.
//     Tra cuu cong thuc cho san pham da phuc vu va goi inventory-service de tru nguyen lieu.

using FnbEngine.Domain.AggregatesModel.RecipeAggregate;
using FnbEngine.Domain.AggregatesModel.SessionAggregate;
using FnbEngine.Domain.Events;
using FnbEngine.Infrastructure.ExternalServices;
using MediatR;
using Microsoft.Extensions.Logging;

namespace FnbEngine.API.Application.IntegrationEvents.EventHandlers;

/// <summary>
/// EN: Handles KitchenTicketServedDomainEvent by looking up recipe and deducting inventory.
/// VI: Xu ly KitchenTicketServedDomainEvent bang cach tra cuu cong thuc va tru kho.
/// </summary>
public class KitchenTicketServedDomainEventHandler : INotificationHandler<KitchenTicketServedDomainEvent>
{
    private readonly IRecipeRepository _recipeRepository;
    private readonly ISessionRepository _sessionRepository;
    private readonly IInventoryServiceClient _inventoryClient;
    private readonly ILogger<KitchenTicketServedDomainEventHandler> _logger;

    public KitchenTicketServedDomainEventHandler(
        IRecipeRepository recipeRepository,
        ISessionRepository sessionRepository,
        IInventoryServiceClient inventoryClient,
        ILogger<KitchenTicketServedDomainEventHandler> logger)
    {
        _recipeRepository = recipeRepository ?? throw new ArgumentNullException(nameof(recipeRepository));
        _sessionRepository = sessionRepository ?? throw new ArgumentNullException(nameof(sessionRepository));
        _inventoryClient = inventoryClient ?? throw new ArgumentNullException(nameof(inventoryClient));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    public async Task Handle(KitchenTicketServedDomainEvent notification, CancellationToken cancellationToken)
    {
        var ticket = notification.Ticket;

        _logger.LogInformation(
            "EN: Kitchen ticket served, starting inventory deduction / VI: Phieu bep da phuc vu, bat dau tru kho - TicketId: {TicketId}, Product: {ProductName}",
            ticket.Id, ticket.ItemName);

        try
        {
            // EN: Get shop ID from the session.
            // VI: Lay shop ID tu session.
            var session = await _sessionRepository.GetByIdAsync(ticket.SessionId, cancellationToken);
            if (session == null)
            {
                _logger.LogWarning(
                    "EN: Session not found for ticket, skipping inventory deduction / VI: Khong tim thay session cho phieu, bo qua tru kho - SessionId: {SessionId}",
                    ticket.SessionId);
                return;
            }

            // EN: Look up the recipe for this product in this shop.
            // VI: Tra cuu cong thuc cho san pham nay trong shop nay.
            var recipe = await _recipeRepository.GetByProductIdAndShopAsync(ticket.ProductId, session.ShopId, cancellationToken);
            if (recipe == null)
            {
                _logger.LogInformation(
                    "EN: No recipe found for product, skipping inventory deduction / VI: Khong tim thay cong thuc cho san pham, bo qua tru kho - ProductId: {ProductId}, ShopId: {ShopId}",
                    ticket.ProductId, session.ShopId);
                return;
            }

            // EN: Filter ingredients that have inventory item links.
            // VI: Loc nguyen lieu co lien ket den inventory item.
            var linkedIngredients = recipe.Ingredients
                .Where(i => i.InventoryItemId.HasValue && i.InventoryItemId.Value != Guid.Empty)
                .ToList();

            if (linkedIngredients.Count == 0)
            {
                _logger.LogInformation(
                    "EN: Recipe has no linked inventory items, skipping deduction / VI: Cong thuc khong co nguyen lieu lien ket kho, bo qua tru kho - RecipeId: {RecipeId}",
                    recipe.Id);
                return;
            }

            // EN: Build deduction request. Multiply quantity per serving by ticket quantity.
            // VI: Tao yeu cau tru kho. Nhan so luong moi phan voi so luong phieu.
            var deductionItems = linkedIngredients.Select(ingredient => new DeductionItemDto(
                ingredient.InventoryItemId!.Value,
                ingredient.QuantityPerServing > 0
                    ? (int)Math.Ceiling(ingredient.QuantityPerServing * ticket.Quantity)
                    : ticket.Quantity,
                ingredient.Unit,
                ingredient.IngredientName
            )).ToList();

            var request = new DeductInventoryRequest(
                ShopId: session.ShopId,
                ReferenceId: ticket.Id,
                ReferenceType: "KitchenTicket",
                Reason: $"Auto-deduction for served ticket: {ticket.ItemName} x{ticket.Quantity}",
                Items: deductionItems);

            // EN: Call inventory service synchronously within the handler.
            //     Polly retry policy on InventoryServiceClient handles transient failures.
            //     If deduction fails, log error but don't throw to avoid blocking kitchen workflow.
            // VI: Goi inventory service dong bo trong handler.
            //     Polly retry policy tren InventoryServiceClient xu ly loi tam thoi.
            //     Neu tru kho that bai, log loi nhung khong throw de tranh chan luong bep.
            try
            {
                var result = await _inventoryClient.DeductInventoryAsync(request, cancellationToken);
                if (result)
                {
                    _logger.LogInformation(
                        "EN: Inventory deduction successful / VI: Tru kho thanh cong - TicketId: {TicketId}, Items: {ItemCount}",
                        ticket.Id, deductionItems.Count);
                }
                else
                {
                    _logger.LogWarning(
                        "EN: Inventory deduction returned false / VI: Tru kho tra ve false - TicketId: {TicketId}",
                        ticket.Id);
                }
            }
            catch (Exception ex)
            {
                // EN: Log but don't throw - inventory deduction should not block kitchen flow.
                // VI: Log nhung khong throw - tru kho khong nen chan luong bep.
                _logger.LogError(ex,
                    "EN: Failed to deduct inventory / VI: Tru kho that bai - TicketId: {TicketId}, Error: {Error}",
                    ticket.Id, ex.Message);
            }
        }
        catch (Exception ex)
        {
            // EN: Log but don't throw - inventory deduction should NOT block kitchen workflow.
            // VI: Log nhung khong throw - tru kho KHONG nen chan luong bep.
            _logger.LogError(ex,
                "EN: Error during inventory deduction handling / VI: Loi trong xu ly tru kho - TicketId: {TicketId}",
                ticket.Id);
        }
    }
}
