using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TakeoutSaaS.Application.App.Stores.Commands;
using TakeoutSaaS.Application.App.Stores.Dto;
using TakeoutSaaS.Application.App.Stores.Queries;
using TakeoutSaaS.Application.App.Stores.Services;
using TakeoutSaaS.Domain.Stores.Entities;
using TakeoutSaaS.Domain.Stores.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.Store;

namespace TakeoutSaaS.TenantApi.Controllers;

/// <summary>
/// 门店费用设置模块。
/// </summary>
[ApiVersion("1.0")]
[Authorize]
[Route("api/tenant/v{version:apiVersion}/store")]
public sealed class StoreFeesController(
    IMediator mediator,
    TakeoutAppDbContext dbContext,
    StoreContextService storeContextService) : BaseApiController
{
    /// <summary>
    /// 获取门店费用设置。
    /// </summary>
    [HttpGet("fees")]
    [ProducesResponseType(typeof(ApiResponse<StoreFeesSettingsDto>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<StoreFeesSettingsDto>> Get([FromQuery] string storeId, CancellationToken cancellationToken)
    {
        var parsedStoreId = StoreApiHelpers.ParseRequiredSnowflake(storeId, "storeId");
        var (tenantId, merchantId) = StoreApiHelpers.GetTenantMerchantContext(storeContextService);
        await StoreApiHelpers.EnsureStoreAccessibleAsync(dbContext, tenantId, merchantId, parsedStoreId, cancellationToken);

        var fee = await mediator.Send(new GetStoreFeeQuery
        {
            StoreId = parsedStoreId
        }, cancellationToken);

        var response = MapFeeSettings(parsedStoreId, fee);
        return ApiResponse<StoreFeesSettingsDto>.Ok(response);
    }

    /// <summary>
    /// 保存门店费用设置。
    /// </summary>
    [HttpPost("fees/save")]
    [ProducesResponseType(typeof(ApiResponse<StoreFeesSettingsDto>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<StoreFeesSettingsDto>> Save([FromBody] StoreFeesSettingsDto request, CancellationToken cancellationToken)
    {
        var parsedStoreId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, "storeId");
        var (tenantId, merchantId) = StoreApiHelpers.GetTenantMerchantContext(storeContextService);
        await StoreApiHelpers.EnsureStoreAccessibleAsync(dbContext, tenantId, merchantId, parsedStoreId, cancellationToken);

        var result = await mediator.Send(new UpdateStoreFeeCommand
        {
            StoreId = parsedStoreId,
            MinimumOrderAmount = request.MinimumOrderAmount,
            DeliveryFee = request.BaseDeliveryFee,
            PlatformServiceRate = request.PlatformServiceRate,
            FreeDeliveryThreshold = request.FreeDeliveryThreshold,
            PackagingFeeMode = ParsePackagingFeeMode(request.PackagingFeeMode),
            OrderPackagingFeeMode = ParseOrderPackagingFeeMode(request.OrderPackagingFeeMode),
            FixedPackagingFee = request.FixedPackagingFee,
            PackagingFeeTiers = (request.PackagingFeeTiers ?? [])
                .OrderBy(x => x.Sort)
                .ThenBy(x => x.MinAmount)
                .Select(x => new StoreFeeTierDto
                {
                    MinPrice = x.MinAmount,
                    MaxPrice = x.MaxAmount,
                    Fee = x.Fee
                })
                .ToList(),
            CutleryFeeEnabled = request.OtherFees.Cutlery.Enabled,
            CutleryFeeAmount = request.OtherFees.Cutlery.Amount,
            RushFeeEnabled = request.OtherFees.Rush.Enabled,
            RushFeeAmount = request.OtherFees.Rush.Amount
        }, cancellationToken);

        return ApiResponse<StoreFeesSettingsDto>.Ok(MapFeeSettings(parsedStoreId, result));
    }

    /// <summary>
    /// 保存包装费收取方式。
    /// </summary>
    [HttpPost("fees/mode/save")]
    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<object>> SaveMode([FromBody] SaveStoreFeesModeRequest request, CancellationToken cancellationToken)
    {
        var parsedStoreId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, "storeId");
        var packagingFeeMode = ParsePackagingFeeMode(request.PackagingFeeMode);
        var (tenantId, merchantId) = StoreApiHelpers.GetTenantMerchantContext(storeContextService);
        var store = await StoreApiHelpers.EnsureStoreAccessibleAsync(dbContext, tenantId, merchantId, parsedStoreId, cancellationToken);

        var fee = await dbContext.StoreFees
            .FirstOrDefaultAsync(x => x.TenantId == tenantId && x.StoreId == parsedStoreId, cancellationToken);
        if (fee is null)
        {
            fee = new StoreFee
            {
                StoreId = parsedStoreId,
                TenantId = store.TenantId
            };
            await dbContext.StoreFees.AddAsync(fee, cancellationToken);
        }

        fee.PackagingFeeMode = packagingFeeMode;
        if (packagingFeeMode == PackagingFeeMode.PerItem)
        {
            fee.OrderPackagingFeeMode = OrderPackagingFeeMode.Fixed;
        }

        await dbContext.SaveChangesAsync(cancellationToken);
        return ApiResponse<object>.Ok(null);
    }

    /// <summary>
    /// 复制费用设置。
    /// </summary>
    [HttpPost("fees/copy")]
    [ProducesResponseType(typeof(ApiResponse<CopyStoreFeesSettingsResult>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<CopyStoreFeesSettingsResult>> Copy([FromBody] CopyStoreFeesSettingsRequest request, CancellationToken cancellationToken)
    {
        var sourceStoreId = StoreApiHelpers.ParseRequiredSnowflake(request.SourceStoreId, "sourceStoreId");
        var targetStoreIds = StoreApiHelpers.ParseSnowflakeList(request.TargetStoreIds);
        var (tenantId, merchantId) = StoreApiHelpers.GetTenantMerchantContext(storeContextService);
        await StoreApiHelpers.EnsureStoreAccessibleAsync(dbContext, tenantId, merchantId, sourceStoreId, cancellationToken);

        var accessibleTargetIds = await StoreApiHelpers.FilterAccessibleStoreIdsAsync(
            dbContext,
            tenantId,
            merchantId,
            targetStoreIds,
            cancellationToken);
        accessibleTargetIds.Remove(sourceStoreId);

        if (accessibleTargetIds.Count == 0)
        {
            return ApiResponse<CopyStoreFeesSettingsResult>.Ok(new CopyStoreFeesSettingsResult
            {
                CopiedCount = 0
            });
        }

        var sourceFee = await dbContext.StoreFees
            .AsNoTracking()
            .FirstOrDefaultAsync(x => x.TenantId == tenantId && x.StoreId == sourceStoreId, cancellationToken);
        if (sourceFee is null)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "源门店未配置费用设置，无法复制");
        }

        var targetFees = await dbContext.StoreFees
            .Where(x => x.TenantId == tenantId && accessibleTargetIds.Contains(x.StoreId))
            .ToListAsync(cancellationToken);
        var targetFeeMap = targetFees.ToDictionary(x => x.StoreId);

        foreach (var targetStoreId in accessibleTargetIds)
        {
            if (!targetFeeMap.TryGetValue(targetStoreId, out var targetFee))
            {
                targetFee = new StoreFee
                {
                    StoreId = targetStoreId
                };
                await dbContext.StoreFees.AddAsync(targetFee, cancellationToken);
            }

            targetFee.MinimumOrderAmount = sourceFee.MinimumOrderAmount;
            targetFee.BaseDeliveryFee = sourceFee.BaseDeliveryFee;
            targetFee.PlatformServiceRate = sourceFee.PlatformServiceRate;
            targetFee.FreeDeliveryThreshold = sourceFee.FreeDeliveryThreshold;
            targetFee.PackagingFeeMode = sourceFee.PackagingFeeMode;
            targetFee.OrderPackagingFeeMode = sourceFee.OrderPackagingFeeMode;
            targetFee.FixedPackagingFee = sourceFee.FixedPackagingFee;
            targetFee.PackagingFeeTiersJson = sourceFee.PackagingFeeTiersJson;
            targetFee.CutleryFeeEnabled = sourceFee.CutleryFeeEnabled;
            targetFee.CutleryFeeAmount = sourceFee.CutleryFeeAmount;
            targetFee.RushFeeEnabled = sourceFee.RushFeeEnabled;
            targetFee.RushFeeAmount = sourceFee.RushFeeAmount;
        }

        await dbContext.SaveChangesAsync(cancellationToken);

        return ApiResponse<CopyStoreFeesSettingsResult>.Ok(new CopyStoreFeesSettingsResult
        {
            CopiedCount = accessibleTargetIds.Count
        });
    }

    private static StoreFeesSettingsDto MapFeeSettings(long storeId, StoreFeeDto? source)
    {
        var tiers = (source?.PackagingFeeTiers ?? [])
            .OrderBy(x => x.MinPrice)
            .Select((tier, index) => new PackagingFeeTierDto
            {
                Id = $"tier-{index + 1}",
                MinAmount = tier.MinPrice,
                MaxAmount = tier.MaxPrice,
                Fee = tier.Fee,
                Sort = index + 1
            })
            .ToList();

        return new StoreFeesSettingsDto
        {
            StoreId = storeId.ToString(),
            IsConfigured = source is not null,
            MinimumOrderAmount = source?.MinimumOrderAmount ?? 0m,
            BaseDeliveryFee = source?.DeliveryFee ?? 0m,
            PlatformServiceRate = source?.PlatformServiceRate ?? 0m,
            FreeDeliveryThreshold = source?.FreeDeliveryThreshold,
            PackagingFeeMode = ToPackagingFeeModeText(source?.PackagingFeeMode ?? PackagingFeeMode.Fixed),
            OrderPackagingFeeMode = ToOrderPackagingFeeModeText(source?.OrderPackagingFeeMode ?? OrderPackagingFeeMode.Fixed),
            FixedPackagingFee = source?.FixedPackagingFee ?? 0m,
            PackagingFeeTiers = tiers,
            OtherFees = new StoreOtherFeesDto
            {
                Cutlery = new AdditionalFeeItemDto
                {
                    Enabled = source?.CutleryFeeEnabled ?? false,
                    Amount = source?.CutleryFeeAmount ?? 0m
                },
                Rush = new AdditionalFeeItemDto
                {
                    Enabled = source?.RushFeeEnabled ?? false,
                    Amount = source?.RushFeeAmount ?? 0m
                }
            }
        };
    }

    private static PackagingFeeMode ParsePackagingFeeMode(string? value)
    {
        if (string.Equals(value, "item", StringComparison.OrdinalIgnoreCase))
        {
            return PackagingFeeMode.PerItem;
        }

        if (string.Equals(value, "order", StringComparison.OrdinalIgnoreCase))
        {
            return PackagingFeeMode.Fixed;
        }

        throw new BusinessException(ErrorCodes.BadRequest, "packagingFeeMode 非法");
    }

    private static string ToPackagingFeeModeText(PackagingFeeMode value)
    {
        return value == PackagingFeeMode.PerItem ? "item" : "order";
    }

    private static OrderPackagingFeeMode ParseOrderPackagingFeeMode(string? value)
    {
        if (string.Equals(value, "tiered", StringComparison.OrdinalIgnoreCase))
        {
            return OrderPackagingFeeMode.Tiered;
        }

        if (string.Equals(value, "fixed", StringComparison.OrdinalIgnoreCase))
        {
            return OrderPackagingFeeMode.Fixed;
        }

        throw new BusinessException(ErrorCodes.BadRequest, "orderPackagingFeeMode 非法");
    }

    private static string ToOrderPackagingFeeModeText(OrderPackagingFeeMode value)
    {
        return value == OrderPackagingFeeMode.Tiered ? "tiered" : "fixed";
    }
}
