using System.Globalization;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using TakeoutSaaS.Application.App.Orders.Dto;
using TakeoutSaaS.Application.App.Orders.Queries;
using TakeoutSaaS.Application.App.Stores.Services;
using TakeoutSaaS.Domain.Orders.Enums;
using TakeoutSaaS.Domain.Payments.Enums;
using TakeoutSaaS.Infrastructure.App.Persistence;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
using TakeoutSaaS.Shared.Abstractions.Results;
using TakeoutSaaS.Shared.Web.Api;
using TakeoutSaaS.TenantApi.Contracts.Order;

namespace TakeoutSaaS.TenantApi.Controllers;

/// <summary>
/// 租户端订单管理。
/// </summary>
[ApiVersion("1.0")]
[Authorize]
[Route("api/tenant/v{version:apiVersion}/order")]
public sealed class OrderController(
    IMediator mediator,
    TakeoutAppDbContext dbContext,
    StoreContextService storeContextService) : BaseApiController
{
    /// <summary>
    /// 全部订单列表。
    /// </summary>
    [HttpGet("all/list")]
    [ProducesResponseType(typeof(ApiResponse<OrderAllListResultResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<OrderAllListResultResponse>> List(
        [FromQuery] OrderAllListRequest request,
        CancellationToken cancellationToken)
    {
        var (storeId, startAt, endAt, status, refundedOnly, deliveryType, paymentMethod) =
            await ParseFilterAsync(request, cancellationToken);

        var result = await mediator.Send(new SearchOrderAllListQuery
        {
            StoreId = storeId,
            StartAt = startAt,
            EndAt = endAt,
            Status = status,
            RefundedOnly = refundedOnly,
            DeliveryType = deliveryType,
            PaymentMethod = paymentMethod,
            Keyword = request.Keyword,
            Page = Math.Max(1, request.Page),
            PageSize = Math.Clamp(request.PageSize, 1, 200),
            SortBy = "createdAt",
            SortDescending = true
        }, cancellationToken);

        return ApiResponse<OrderAllListResultResponse>.Ok(new OrderAllListResultResponse
        {
            Items = result.Items.Select(MapListItem).ToList(),
            Total = result.TotalCount,
            Page = result.Page,
            PageSize = result.PageSize
        });
    }

    /// <summary>
    /// 全部订单统计。
    /// </summary>
    [HttpGet("all/stats")]
    [ProducesResponseType(typeof(ApiResponse<OrderAllStatsResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<OrderAllStatsResponse>> Stats(
        [FromQuery] OrderAllFilterRequest request,
        CancellationToken cancellationToken)
    {
        var (storeId, startAt, endAt, status, refundedOnly, deliveryType, paymentMethod) =
            await ParseFilterAsync(request, cancellationToken);

        var result = await mediator.Send(new GetOrderAllStatsQuery
        {
            StoreId = storeId,
            StartAt = startAt,
            EndAt = endAt,
            Status = status,
            RefundedOnly = refundedOnly,
            DeliveryType = deliveryType,
            PaymentMethod = paymentMethod,
            Keyword = request.Keyword
        }, cancellationToken);

        return ApiResponse<OrderAllStatsResponse>.Ok(new OrderAllStatsResponse
        {
            TotalOrders = result.TotalOrders,
            TotalAmount = result.TotalAmount,
            AverageAmount = result.AverageAmount,
            RefundCount = result.RefundCount
        });
    }

    /// <summary>
    /// 全部订单详情。
    /// </summary>
    [HttpGet("all/detail")]
    [ProducesResponseType(typeof(ApiResponse<OrderAllDetailResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<OrderAllDetailResponse>> Detail(
        [FromQuery] OrderAllDetailRequest request,
        CancellationToken cancellationToken)
    {
        var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(storeId, cancellationToken);

        var orderNo = request.OrderNo?.Trim() ?? string.Empty;
        if (string.IsNullOrWhiteSpace(orderNo))
        {
            return ApiResponse<OrderAllDetailResponse>.Error(ErrorCodes.BadRequest, "orderNo 非法");
        }

        var result = await mediator.Send(new GetOrderAllDetailQuery
        {
            StoreId = storeId,
            OrderNo = orderNo
        }, cancellationToken);

        if (result is null)
        {
            return ApiResponse<OrderAllDetailResponse>.Error(ErrorCodes.NotFound, "订单不存在");
        }

        return ApiResponse<OrderAllDetailResponse>.Ok(MapDetail(result));
    }

    /// <summary>
    /// 全部订单导出。
    /// </summary>
    [HttpGet("all/export")]
    [ProducesResponseType(typeof(ApiResponse<OrderAllExportResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<OrderAllExportResponse>> Export(
        [FromQuery] OrderAllFilterRequest request,
        CancellationToken cancellationToken)
    {
        var (storeId, startAt, endAt, status, refundedOnly, deliveryType, paymentMethod) =
            await ParseFilterAsync(request, cancellationToken);

        var result = await mediator.Send(new ExportOrderAllCsvQuery
        {
            StoreId = storeId,
            StartAt = startAt,
            EndAt = endAt,
            Status = status,
            RefundedOnly = refundedOnly,
            DeliveryType = deliveryType,
            PaymentMethod = paymentMethod,
            Keyword = request.Keyword
        }, cancellationToken);

        return ApiResponse<OrderAllExportResponse>.Ok(new OrderAllExportResponse
        {
            FileName = result.FileName,
            FileContentBase64 = result.FileContentBase64,
            TotalCount = result.TotalCount
        });
    }

    private async Task<(long StoreId, DateTime? StartAt, DateTime? EndAt, OrderStatus? Status, bool RefundedOnly, DeliveryType? DeliveryType, PaymentMethod? PaymentMethod)> ParseFilterAsync(
        OrderAllFilterRequest request,
        CancellationToken cancellationToken)
    {
        var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(storeId, cancellationToken);

        var startAt = ParseDateOrNull(request.StartDate);
        var endAt = ParseDateOrNull(request.EndDate)?.AddDays(1);
        if (startAt.HasValue && endAt.HasValue && startAt >= endAt)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "开始日期不能晚于结束日期");
        }

        var (status, refundedOnly) = ParseStatus(request.Status);
        var deliveryType = ParseDeliveryType(request.Channel);
        if (deliveryType is null)
        {
            var normalizedStatus = (request.Status ?? string.Empty).Trim().ToLowerInvariant();
            if (normalizedStatus == "pickup")
            {
                deliveryType = Domain.Orders.Enums.DeliveryType.Pickup;
            }
            else if (normalizedStatus == "delivering")
            {
                deliveryType = Domain.Orders.Enums.DeliveryType.Delivery;
            }
        }
        var paymentMethod = ParsePaymentMethod(request.PaymentMethod);

        return (storeId, startAt, endAt, status, refundedOnly, deliveryType, paymentMethod);
    }

    private async Task EnsureStoreAccessibleAsync(long storeId, CancellationToken cancellationToken)
    {
        var (tenantId, merchantId) = StoreApiHelpers.GetTenantMerchantContext(storeContextService);
        await StoreApiHelpers.EnsureStoreAccessibleAsync(dbContext, tenantId, merchantId, storeId, cancellationToken);
    }

    private static DateTime? ParseDateOrNull(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return null;
        }

        if (DateTime.TryParseExact(
                value,
                "yyyy-MM-dd",
                CultureInfo.InvariantCulture,
                DateTimeStyles.None,
                out var parsed))
        {
            return DateTime.SpecifyKind(parsed.Date, DateTimeKind.Utc);
        }

        throw new BusinessException(ErrorCodes.BadRequest, "日期格式必须为 yyyy-MM-dd");
    }

    private static (OrderStatus? Status, bool RefundedOnly) ParseStatus(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "pending" => (OrderStatus.AwaitingPreparation, false),
            "making" => (OrderStatus.InProgress, false),
            "delivering" => (OrderStatus.Ready, false),
            "pickup" => (OrderStatus.Ready, false),
            "completed" => (OrderStatus.Completed, false),
            "cancelled" => (OrderStatus.Cancelled, false),
            "refunded" => (null, true),
            _ => (null, false)
        };
    }

    private static DeliveryType? ParseDeliveryType(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "delivery" => Domain.Orders.Enums.DeliveryType.Delivery,
            "pickup" => Domain.Orders.Enums.DeliveryType.Pickup,
            "dine_in" => Domain.Orders.Enums.DeliveryType.DineIn,
            _ => null
        };
    }

    private static PaymentMethod? ParsePaymentMethod(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "wechat" => Domain.Payments.Enums.PaymentMethod.WeChatPay,
            "alipay" => Domain.Payments.Enums.PaymentMethod.Alipay,
            "balance" => Domain.Payments.Enums.PaymentMethod.Balance,
            "cash" => Domain.Payments.Enums.PaymentMethod.Cash,
            "card" => Domain.Payments.Enums.PaymentMethod.Card,
            _ => null
        };
    }

    private static OrderAllListItemResponse MapListItem(OrderAllListItemDto source)
    {
        return new OrderAllListItemResponse
        {
            OrderNo = source.OrderNo,
            OrderedAt = source.OrderedAt.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture),
            Channel = ToDeliveryTypeText(source.DeliveryType),
            Customer = source.CustomerName,
            ItemsSummary = source.ItemsSummary,
            Amount = source.Amount,
            Status = ToStatusText(source.Status, source.IsRefunded, source.DeliveryType),
            IsDimmed = source.IsDimmed
        };
    }

    private static OrderAllDetailResponse MapDetail(OrderAllDetailDto source)
    {
        return new OrderAllDetailResponse
        {
            OrderNo = source.OrderNo,
            Channel = ToDeliveryTypeText(source.DeliveryType),
            Status = ToStatusText(source.Status, false, source.DeliveryType),
            PaymentMethod = ToPaymentMethodText(source.PaymentMethod),
            OrderedAt = source.OrderedAt.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture),
            PaidAt = source.PaidAt?.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture),
            FinishedAt = source.FinishedAt?.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture),
            CustomerName = source.CustomerName,
            CustomerPhone = source.CustomerPhone,
            CustomerAddress = source.CustomerAddress,
            ItemsAmount = source.ItemsAmount,
            DeliveryFee = source.DeliveryFee,
            DiscountAmount = source.DiscountAmount,
            PaidAmount = source.PaidAmount,
            Remark = source.Remark,
            Items = source.Items
                .Select(item => new OrderAllDetailItemResponse
                {
                    Name = item.Name,
                    Spec = item.Spec,
                    Quantity = item.Quantity,
                    UnitPrice = item.UnitPrice,
                    SubTotal = item.SubTotal
                })
                .ToList(),
            Timeline = source.Timeline
                .OrderBy(item => item.OccurredAt)
                .Select(item => new OrderAllTimelineResponse
                {
                    Label = item.Label,
                    Time = item.OccurredAt.ToString("HH:mm:ss", CultureInfo.InvariantCulture)
                })
                .ToList()
        };
    }

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

    private static string ToPaymentMethodText(Domain.Payments.Enums.PaymentMethod value)
    {
        return value switch
        {
            Domain.Payments.Enums.PaymentMethod.WeChatPay => "微信支付",
            Domain.Payments.Enums.PaymentMethod.Alipay => "支付宝",
            Domain.Payments.Enums.PaymentMethod.Balance => "余额支付",
            Domain.Payments.Enums.PaymentMethod.Cash => "现金",
            Domain.Payments.Enums.PaymentMethod.Card => "刷卡",
            _ => "--"
        };
    }

    private static string ToStatusText(OrderStatus status, bool refunded, DeliveryType deliveryType)
    {
        if (refunded)
        {
            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 => "待取餐",
            _ => "待处理"
        };
    }
}
