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

namespace TakeoutSaaS.TenantApi.Controllers;

/// <summary>
/// 财务中心交易流水。
/// </summary>
[ApiVersion("1.0")]
[Authorize]
[Route("api/tenant/v{version:apiVersion}/finance/transaction")]
public sealed class FinanceTransactionController(
    IMediator mediator,
    TakeoutAppDbContext dbContext,
    StoreContextService storeContextService) : BaseApiController
{
    private const string ViewPermission = "tenant:finance:transaction:view";
    private const string DetailPermission = "tenant:finance:transaction:detail";
    private const string ExportPermission = "tenant:finance:transaction:export";

    /// <summary>
    /// 查询交易流水列表。
    /// </summary>
    [HttpGet("list")]
    [PermissionAuthorize(ViewPermission)]
    [ProducesResponseType(typeof(ApiResponse<FinanceTransactionListResultResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<FinanceTransactionListResultResponse>> List(
        [FromQuery] FinanceTransactionListRequest request,
        CancellationToken cancellationToken)
    {
        // 1. 解析并校验筛选参数。
        var parsed = await ParseFilterAsync(request, cancellationToken);

        // 2. 发起查询并映射响应。
        var result = await mediator.Send(new SearchFinanceTransactionListQuery
        {
            StoreId = parsed.StoreId,
            StartAt = parsed.StartAt,
            EndAt = parsed.EndAt,
            TransactionType = parsed.TransactionType,
            DeliveryType = parsed.DeliveryType,
            PaymentMethod = parsed.PaymentMethod,
            Keyword = request.Keyword,
            Page = Math.Max(1, request.Page),
            PageSize = Math.Clamp(request.PageSize, 1, 200)
        }, cancellationToken);

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

    /// <summary>
    /// 查询交易流水统计。
    /// </summary>
    [HttpGet("stats")]
    [PermissionAuthorize(ViewPermission)]
    [ProducesResponseType(typeof(ApiResponse<FinanceTransactionStatsResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<FinanceTransactionStatsResponse>> Stats(
        [FromQuery] FinanceTransactionFilterRequest request,
        CancellationToken cancellationToken)
    {
        // 1. 解析并校验筛选参数。
        var parsed = await ParseFilterAsync(request, cancellationToken);

        // 2. 发起查询并映射响应。
        var result = await mediator.Send(new GetFinanceTransactionStatsQuery
        {
            StoreId = parsed.StoreId,
            StartAt = parsed.StartAt,
            EndAt = parsed.EndAt,
            TransactionType = parsed.TransactionType,
            DeliveryType = parsed.DeliveryType,
            PaymentMethod = parsed.PaymentMethod,
            Keyword = request.Keyword
        }, cancellationToken);

        return ApiResponse<FinanceTransactionStatsResponse>.Ok(new FinanceTransactionStatsResponse
        {
            TotalIncome = result.TotalIncome,
            TotalRefund = result.TotalRefund,
            TotalCount = result.TotalCount
        });
    }

    /// <summary>
    /// 查询交易流水详情。
    /// </summary>
    [HttpGet("detail")]
    [PermissionAuthorize(ViewPermission, DetailPermission)]
    [ProducesResponseType(typeof(ApiResponse<FinanceTransactionDetailResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<FinanceTransactionDetailResponse>> Detail(
        [FromQuery] FinanceTransactionDetailRequest request,
        CancellationToken cancellationToken)
    {
        // 1. 校验门店参数与门店访问权限。
        var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(storeId, cancellationToken);

        // 2. 解析交易复合标识。
        if (!TryParseTransactionId(request.TransactionId, out var sourceType, out var sourceId))
        {
            return ApiResponse<FinanceTransactionDetailResponse>.Error(ErrorCodes.BadRequest, "transactionId 非法");
        }

        // 3. 查询详情并返回。
        var detail = await mediator.Send(new GetFinanceTransactionDetailQuery
        {
            StoreId = storeId,
            SourceType = sourceType,
            SourceId = sourceId
        }, cancellationToken);

        if (detail is null)
        {
            return ApiResponse<FinanceTransactionDetailResponse>.Error(ErrorCodes.NotFound, "交易流水不存在");
        }

        return ApiResponse<FinanceTransactionDetailResponse>.Ok(MapDetail(detail));
    }

    /// <summary>
    /// 导出交易流水 CSV。
    /// </summary>
    [HttpGet("export")]
    [PermissionAuthorize(ExportPermission)]
    [ProducesResponseType(typeof(ApiResponse<FinanceTransactionExportResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<FinanceTransactionExportResponse>> Export(
        [FromQuery] FinanceTransactionFilterRequest request,
        CancellationToken cancellationToken)
    {
        // 1. 解析并校验筛选参数。
        var parsed = await ParseFilterAsync(request, cancellationToken);

        // 2. 发起导出并返回结果。
        var result = await mediator.Send(new ExportFinanceTransactionCsvQuery
        {
            StoreId = parsed.StoreId,
            StartAt = parsed.StartAt,
            EndAt = parsed.EndAt,
            TransactionType = parsed.TransactionType,
            DeliveryType = parsed.DeliveryType,
            PaymentMethod = parsed.PaymentMethod,
            Keyword = request.Keyword
        }, cancellationToken);

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

    private async Task<(long StoreId, DateTime? StartAt, DateTime? EndAt, FinanceTransactionType? TransactionType, DeliveryType? DeliveryType, PaymentMethod? PaymentMethod)> ParseFilterAsync(
        FinanceTransactionFilterRequest 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 transactionType = ParseTransactionType(request.Type);
        var deliveryType = ParseDeliveryType(request.Channel);
        var paymentMethod = ParsePaymentMethod(request.PaymentMethod);

        return (storeId, startAt, endAt, transactionType, 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 FinanceTransactionType? ParseTransactionType(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "income" => FinanceTransactionType.Income,
            "refund" => FinanceTransactionType.Refund,
            "stored_card_recharge" => FinanceTransactionType.StoredCardRecharge,
            "point_redeem" => FinanceTransactionType.PointRedeem,
            _ => null
        };
    }

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

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

    private static bool TryParseTransactionId(string? value, out FinanceTransactionSourceType sourceType, out long sourceId)
    {
        sourceType = default;
        sourceId = 0;

        var normalized = (value ?? string.Empty).Trim();
        if (string.IsNullOrWhiteSpace(normalized))
        {
            return false;
        }

        var parts = normalized.Split(':', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
        if (parts.Length != 2)
        {
            return false;
        }

        if (!long.TryParse(parts[1], out sourceId) || sourceId <= 0)
        {
            return false;
        }

        sourceType = parts[0].ToLowerInvariant() switch
        {
            "payment" => FinanceTransactionSourceType.PaymentRecord,
            "payment_refund" => FinanceTransactionSourceType.PaymentRefundRecord,
            "refund_request" => FinanceTransactionSourceType.RefundRequest,
            "stored_card_recharge" => FinanceTransactionSourceType.StoredCardRechargeRecord,
            "member_point" => FinanceTransactionSourceType.MemberPointLedger,
            _ => default
        };

        return sourceType != default;
    }

    private static FinanceTransactionListItemResponse MapListItem(FinanceTransactionListItemDto source)
    {
        return new FinanceTransactionListItemResponse
        {
            TransactionId = source.TransactionId,
            TransactionNo = source.TransactionNo,
            OrderNo = source.OrderNo,
            Type = source.TransactionType,
            TypeText = source.TransactionTypeText,
            Channel = source.ChannelText,
            PaymentMethod = source.PaymentMethodText,
            Amount = source.AmountSigned,
            OccurredAt = source.OccurredAt.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture),
            Remark = source.Remark,
            IsIncome = source.IsIncome
        };
    }

    private static FinanceTransactionDetailResponse MapDetail(FinanceTransactionDetailDto source)
    {
        return new FinanceTransactionDetailResponse
        {
            TransactionId = source.TransactionId,
            TransactionNo = source.TransactionNo,
            Type = source.TransactionType,
            TypeText = source.TransactionTypeText,
            StoreId = source.StoreId.ToString(),
            OrderNo = source.OrderNo,
            Channel = source.ChannelText,
            PaymentMethod = source.PaymentMethodText,
            Amount = source.AmountSigned,
            OccurredAt = source.OccurredAt.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture),
            Remark = source.Remark,
            CustomerName = source.CustomerName,
            CustomerPhone = source.CustomerPhone,
            RefundNo = source.RefundNo,
            RefundReason = source.RefundReason,
            MemberName = source.MemberName,
            MemberMobileMasked = source.MemberMobileMasked,
            RechargeAmount = source.RechargeAmount,
            GiftAmount = source.GiftAmount,
            ArrivedAmount = source.ArrivedAmount,
            PointChangeAmount = source.PointChangeAmount,
            PointBalanceAfterChange = source.PointBalanceAfterChange
        };
    }
}
