using System.Globalization;
using System.Text;
using MediatR;
using TakeoutSaaS.Application.App.Orders.Dto;
using TakeoutSaaS.Application.App.Orders.Queries;
using TakeoutSaaS.Domain.Orders.Entities;
using TakeoutSaaS.Domain.Orders.Enums;
using TakeoutSaaS.Domain.Orders.Repositories;
using TakeoutSaaS.Shared.Abstractions.Tenancy;

namespace TakeoutSaaS.Application.App.Orders.Handlers;

/// <summary>
/// 全部订单 CSV 导出处理器。
/// </summary>
public sealed class ExportOrderAllCsvQueryHandler(
    IOrderRepository orderRepository,
    ITenantProvider tenantProvider)
    : IRequestHandler<ExportOrderAllCsvQuery, OrderAllExportDto>
{
    /// <inheritdoc />
    public async Task<OrderAllExportDto> Handle(ExportOrderAllCsvQuery request, CancellationToken cancellationToken)
    {
        var tenantId = tenantProvider.GetCurrentTenantId();

        var orders = (await orderRepository.SearchAllOrdersAsync(
                tenantId,
                request.StoreId,
                request.StartAt,
                request.EndAt,
                request.Status,
                request.DeliveryType,
                request.PaymentMethod,
                request.Keyword,
                cancellationToken))
            .OrderByDescending(order => order.CreatedAt)
            .ToList();

        var refundedIds = await LoadRefundedOrderIdsAsync(orders, tenantId, cancellationToken);
        if (request.RefundedOnly)
        {
            orders = orders.Where(order => refundedIds.Contains(order.Id)).ToList();
        }

        var itemsLookup = await orderRepository.GetItemsByOrderIdsAsync(
            orders.Select(order => order.Id).ToList(),
            tenantId,
            cancellationToken);

        var csv = BuildCsv(orders, refundedIds, itemsLookup);
        var bytes = Encoding.UTF8.GetPreamble().Concat(Encoding.UTF8.GetBytes(csv)).ToArray();

        return new OrderAllExportDto
        {
            FileName = $"全部订单_{DateTime.UtcNow:yyyyMMddHHmmss}.csv",
            FileContentBase64 = Convert.ToBase64String(bytes),
            TotalCount = orders.Count
        };
    }

    private async Task<HashSet<long>> LoadRefundedOrderIdsAsync(
        IReadOnlyCollection<Order> orders,
        long tenantId,
        CancellationToken cancellationToken)
    {
        var orderIds = orders.Select(order => order.Id).ToList();
        if (orderIds.Count == 0)
        {
            return [];
        }

        var refunded = await orderRepository.GetRefundedOrderIdsAsync(orderIds, tenantId, cancellationToken);
        return refunded.ToHashSet();
    }

    private static string BuildCsv(
        IReadOnlyList<Order> orders,
        IReadOnlySet<long> refundedIds,
        IReadOnlyDictionary<long, IReadOnlyList<OrderItem>> itemsLookup)
    {
        var sb = new StringBuilder();
        sb.AppendLine("订单号,下单时间,渠道,顾客,商品,金额,状态");

        foreach (var order in orders)
        {
            var isRefunded = refundedIds.Contains(order.Id);
            var row = new[]
            {
                Escape(order.OrderNo),
                Escape(order.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)),
                Escape(ToDeliveryTypeText(order.DeliveryType)),
                Escape(string.IsNullOrWhiteSpace(order.CustomerName) ? "--" : order.CustomerName),
                Escape(BuildItemSummary(order.Id, itemsLookup)),
                Escape(ResolveDisplayAmount(order).ToString("0.00", CultureInfo.InvariantCulture)),
                Escape(ToStatusText(order.Status, isRefunded, order.DeliveryType))
            };

            sb.AppendLine(string.Join(',', row));
        }

        return sb.ToString();
    }

    private static decimal ResolveDisplayAmount(Order order)
    {
        return order.PaidAmount > 0 ? order.PaidAmount : order.PayableAmount;
    }

    private static string BuildItemSummary(
        long orderId,
        IReadOnlyDictionary<long, IReadOnlyList<OrderItem>> itemsLookup)
    {
        if (!itemsLookup.TryGetValue(orderId, out var items) || items.Count == 0)
        {
            return "--";
        }

        var first = string.IsNullOrWhiteSpace(items[0].ProductName) ? "商品" : items[0].ProductName.Trim();
        var totalQuantity = items.Sum(item => Math.Max(0, item.Quantity));
        if (totalQuantity <= 0)
        {
            totalQuantity = items.Count;
        }

        return $"{first}等{totalQuantity}件";
    }

    private static string ToStatusText(OrderStatus status, bool isRefunded, DeliveryType deliveryType)
    {
        if (isRefunded)
        {
            return "已退款";
        }

        return status switch
        {
            OrderStatus.PendingPayment => "待接单",
            OrderStatus.AwaitingPreparation => "待接单",
            OrderStatus.InProgress => "制作中",
            OrderStatus.Ready => ToReadyStatusText(deliveryType),
            OrderStatus.Completed => "已完成",
            OrderStatus.Cancelled => "已取消",
            _ => "未知"
        };
    }

    private static string ToReadyStatusText(DeliveryType deliveryType)
    {
        return deliveryType switch
        {
            DeliveryType.Delivery => "配送中",
            DeliveryType.Pickup => "待取餐",
            DeliveryType.DineIn => "待取餐",
            _ => "待处理"
        };
    }

    private static string ToDeliveryTypeText(DeliveryType type)
    {
        return type switch
        {
            DeliveryType.Delivery => "外卖",
            DeliveryType.Pickup => "自提",
            DeliveryType.DineIn => "堂食",
            _ => "未知"
        };
    }

    private static string Escape(string input)
    {
        if (!input.Contains('"') && !input.Contains(',') && !input.Contains('\n') && !input.Contains('\r'))
        {
            return input;
        }

        return $"\"{input.Replace("\"", "\"\"")}\"";
    }
}
