using MediatR;
using Microsoft.Extensions.Logging;
using TakeoutSaaS.Application.App.Orders.Commands;
using TakeoutSaaS.Application.App.Orders.Dto;
using TakeoutSaaS.Application.Messaging;
using TakeoutSaaS.Application.Messaging.Abstractions;
using TakeoutSaaS.Application.Messaging.Events;
using TakeoutSaaS.Domain.Orders.Entities;
using TakeoutSaaS.Domain.Orders.Repositories;
using TakeoutSaaS.Shared.Abstractions.Ids;

namespace TakeoutSaaS.Application.App.Orders.Handlers;

/// <summary>
/// 创建订单命令处理器。
/// </summary>
public sealed class CreateOrderCommandHandler(
    IOrderRepository orderRepository,
    IIdGenerator idGenerator,
    IEventPublisher eventPublisher,
    ILogger<CreateOrderCommandHandler> logger)
    : IRequestHandler<CreateOrderCommand, OrderDto>
{
    /// <inheritdoc />
    public async Task<OrderDto> Handle(CreateOrderCommand request, CancellationToken cancellationToken)
    {
        // 1. 构建订单
        var order = new Order
        {
            Id = idGenerator.NextId(),
            OrderNo = request.OrderNo.Trim(),
            StoreId = request.StoreId,
            Channel = request.Channel,
            DeliveryType = request.DeliveryType,
            Status = request.Status,
            PaymentStatus = request.PaymentStatus,
            CustomerName = request.CustomerName?.Trim(),
            CustomerPhone = request.CustomerPhone?.Trim(),
            TableNo = request.TableNo?.Trim(),
            QueueNumber = request.QueueNumber?.Trim(),
            ReservationId = request.ReservationId,
            ItemsAmount = request.ItemsAmount,
            DiscountAmount = request.DiscountAmount,
            PayableAmount = request.PayableAmount,
            PaidAmount = request.PaidAmount,
            PaidAt = request.PaidAt,
            FinishedAt = request.FinishedAt,
            CancelledAt = request.CancelledAt,
            CancelReason = request.CancelReason?.Trim(),
            Remark = request.Remark?.Trim()
        };

        // 2. 构建明细
        var items = request.Items.Select(item => new OrderItem
        {
            OrderId = order.Id,
            ProductId = item.ProductId,
            ProductName = item.ProductName.Trim(),
            SkuName = item.SkuName?.Trim(),
            Unit = item.Unit?.Trim(),
            Quantity = item.Quantity,
            UnitPrice = item.UnitPrice,
            DiscountAmount = item.DiscountAmount,
            SubTotal = item.SubTotal,
            AttributesJson = item.AttributesJson?.Trim()
        }).ToList();

        // 3. 补充金额字段
        if (items.Count > 0)
        {
            var itemsAmount = items.Sum(x => x.SubTotal);
            order.ItemsAmount = itemsAmount;
            if (order.PayableAmount <= 0)
            {
                order.PayableAmount = itemsAmount - order.DiscountAmount;
            }
        }

        // 4. 持久化
        await orderRepository.AddOrderAsync(order, cancellationToken);
        if (items.Count > 0)
        {
            await orderRepository.AddItemsAsync(items, cancellationToken);
        }

        await orderRepository.SaveChangesAsync(cancellationToken);

        // 5. 发布订单创建事件
        var itemsSummary = items.Count > 0
            ? string.Join("、", items.Take(3).Select(x => x.ProductName))
              + (items.Count > 3 ? $" 等{items.Count}件" : string.Empty)
            : string.Empty;

        await eventPublisher.PublishAsync(EventRoutingKeys.OrderCreated, new OrderCreatedEvent
        {
            OrderId = order.Id,
            OrderNo = order.OrderNo,
            Amount = order.PaidAmount,
            TenantId = order.TenantId,
            StoreId = order.StoreId,
            Channel = (int)order.Channel,
            DeliveryType = (int)order.DeliveryType,
            CustomerName = order.CustomerName,
            ItemsSummary = itemsSummary,
            TableNo = order.TableNo,
            CreatedAt = order.CreatedAt
        }, cancellationToken);

        // 6. 记录日志
        logger.LogInformation("创建订单 {OrderNo} ({OrderId})", order.OrderNo, order.Id);

        // 7. 返回 DTO
        return MapToDto(order, items, [], []);
    }

    private static OrderDto MapToDto(
        Order order,
        IReadOnlyList<OrderItem> items,
        IReadOnlyList<OrderStatusHistory> histories,
        IReadOnlyList<RefundRequest> refunds) => new()
        {
            Id = order.Id,
            TenantId = order.TenantId,
            OrderNo = order.OrderNo,
            StoreId = order.StoreId,
            Channel = order.Channel,
            DeliveryType = order.DeliveryType,
            Status = order.Status,
            PaymentStatus = order.PaymentStatus,
            CustomerName = order.CustomerName,
            CustomerPhone = order.CustomerPhone,
            TableNo = order.TableNo,
            QueueNumber = order.QueueNumber,
            ReservationId = order.ReservationId,
            ItemsAmount = order.ItemsAmount,
            DiscountAmount = order.DiscountAmount,
            PayableAmount = order.PayableAmount,
            PaidAmount = order.PaidAmount,
            PaidAt = order.PaidAt,
            FinishedAt = order.FinishedAt,
            CancelledAt = order.CancelledAt,
            CancelReason = order.CancelReason,
            Remark = order.Remark,
            Items = items.Select(x => new OrderItemDto
            {
                Id = x.Id,
                OrderId = x.OrderId,
                ProductId = x.ProductId,
                ProductName = x.ProductName,
                SkuName = x.SkuName,
                Unit = x.Unit,
                Quantity = x.Quantity,
                UnitPrice = x.UnitPrice,
                DiscountAmount = x.DiscountAmount,
                SubTotal = x.SubTotal,
                AttributesJson = x.AttributesJson
            }).ToList(),
            StatusHistory = histories.Select(x => new OrderStatusHistoryDto
            {
                Id = x.Id,
                OrderId = x.OrderId,
                Status = x.Status,
                OperatorId = x.OperatorId,
                Notes = x.Notes,
                OccurredAt = x.OccurredAt
            }).ToList(),
            Refunds = refunds.Select(x => new RefundRequestDto
            {
                Id = x.Id,
                OrderId = x.OrderId,
                RefundNo = x.RefundNo,
                Amount = x.Amount,
                Reason = x.Reason,
                Status = x.Status,
                RequestedAt = x.RequestedAt,
                ProcessedAt = x.ProcessedAt,
                ReviewNotes = x.ReviewNotes
            }).ToList(),
            CreatedAt = order.CreatedAt
        };
}
