using System.Globalization;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TakeoutSaaS.Application.App.Coupons.FullReduction.Commands;
using TakeoutSaaS.Application.App.Coupons.FullReduction.Dto;
using TakeoutSaaS.Application.App.Coupons.FullReduction.Queries;
using TakeoutSaaS.Application.App.Stores.Services;
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.Marketing;

namespace TakeoutSaaS.TenantApi.Controllers;

/// <summary>
/// 营销中心满减活动管理。
/// </summary>
[ApiVersion("1.0")]
[Authorize]
[Route("api/tenant/v{version:apiVersion}/marketing/full-reduction")]
public sealed class MarketingFullReductionController(
    IMediator mediator,
    TakeoutAppDbContext dbContext,
    StoreContextService storeContextService) : BaseApiController
{
    /// <summary>
    /// 获取满减活动列表。
    /// </summary>
    [HttpGet("list")]
    [ProducesResponseType(typeof(ApiResponse<FullReductionListResultResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<FullReductionListResultResponse>> List(
        [FromQuery] FullReductionListRequest request,
        CancellationToken cancellationToken)
    {
        // 1. 解析可见门店范围（支持全部门店）
        var visibleStoreIds = await ResolveVisibleStoreIdsAsync(request.StoreId, cancellationToken);

        // 2. 查询应用层列表
        var result = await mediator.Send(new GetFullReductionCampaignListQuery
        {
            VisibleStoreIds = visibleStoreIds,
            Keyword = request.Keyword,
            ActivityType = request.ActivityType,
            Status = request.Status,
            Page = request.Page,
            PageSize = request.PageSize
        }, cancellationToken);

        // 3. 映射响应
        return ApiResponse<FullReductionListResultResponse>.Ok(new FullReductionListResultResponse
        {
            Items = result.Items.Select(MapListItem).ToList(),
            Total = result.TotalCount,
            Page = result.Page,
            PageSize = result.PageSize,
            Stats = new FullReductionStatsResponse
            {
                TotalCount = result.Stats.TotalCount,
                OngoingCount = result.Stats.OngoingCount,
                MonthlyDrivenSalesAmount = result.Stats.MonthlyDrivenSalesAmount,
                AverageTicketIncrease = result.Stats.AverageTicketIncrease
            }
        });
    }

    /// <summary>
    /// 获取满减活动详情。
    /// </summary>
    [HttpGet("detail")]
    [ProducesResponseType(typeof(ApiResponse<FullReductionDetailResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<FullReductionDetailResponse>> Detail(
        [FromQuery] FullReductionDetailRequest request,
        CancellationToken cancellationToken)
    {
        // 1. 解析并校验操作门店
        var operationStoreId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(operationStoreId, cancellationToken);

        // 2. 查询详情
        var detail = await mediator.Send(new GetFullReductionCampaignDetailQuery
        {
            OperationStoreId = operationStoreId,
            CampaignId = StoreApiHelpers.ParseRequiredSnowflake(request.ActivityId, nameof(request.ActivityId))
        }, cancellationToken);

        // 3. 处理不存在场景
        if (detail is null)
        {
            return ApiResponse<FullReductionDetailResponse>.Error(ErrorCodes.NotFound, "活动不存在");
        }

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

    /// <summary>
    /// 保存满减活动（新增/编辑）。
    /// </summary>
    [HttpPost("save")]
    [ProducesResponseType(typeof(ApiResponse<FullReductionDetailResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<FullReductionDetailResponse>> Save(
        [FromBody] SaveFullReductionRequest request,
        CancellationToken cancellationToken)
    {
        // 1. 校验操作门店
        var operationStoreId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(operationStoreId, cancellationToken);

        // 2. 展开活动门店范围
        var resolvedStoreIds = await ResolveStoreScopeStoreIdsAsync(
            request.StoreScopeMode,
            request.StoreIds,
            cancellationToken);

        // 3. 解析选品基准门店
        var scopeStoreId = string.IsNullOrWhiteSpace(request.ScopeStoreId)
            ? operationStoreId
            : StoreApiHelpers.ParseRequiredSnowflake(request.ScopeStoreId, nameof(request.ScopeStoreId));

        if (!resolvedStoreIds.Contains(scopeStoreId))
        {
            throw new BusinessException(ErrorCodes.BadRequest, "scopeStoreId 必须在活动门店范围内");
        }

        // 4. 保存活动
        var result = await mediator.Send(new SaveFullReductionCampaignCommand
        {
            OperationStoreId = operationStoreId,
            CampaignId = StoreApiHelpers.ParseSnowflakeOrNull(request.Id),
            Name = request.Name,
            ActivityType = request.ActivityType,
            ReduceTiers = request.ReduceTiers.Select(item => new FullReductionTierRuleDto
            {
                MeetAmount = item.MeetAmount,
                ReduceAmount = item.ReduceAmount
            }).ToList(),
            GiftRule = MapGiftRuleRequest(request.GiftRule),
            SecondHalfRule = MapSecondHalfRuleRequest(request.SecondHalfRule),
            StartAt = StoreApiHelpers.ParseDateOnly(request.StartDate, nameof(request.StartDate)),
            EndAt = StoreApiHelpers.ParseDateOnly(request.EndDate, nameof(request.EndDate)),
            Channels = request.Channels ?? [],
            StoreScopeMode = request.StoreScopeMode,
            StoreScopeStoreIds = resolvedStoreIds,
            ScopeStoreId = scopeStoreId,
            StackWithCoupon = request.StackWithCoupon,
            Description = request.Description,
            Metrics = MapMetricsRequest(request.Metrics)
        }, cancellationToken);

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

    /// <summary>
    /// 修改满减活动状态。
    /// </summary>
    [HttpPost("status")]
    [ProducesResponseType(typeof(ApiResponse<FullReductionDetailResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<FullReductionDetailResponse>> ChangeStatus(
        [FromBody] ChangeFullReductionStatusRequest request,
        CancellationToken cancellationToken)
    {
        // 1. 校验操作门店
        var operationStoreId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(operationStoreId, cancellationToken);

        // 2. 调用应用层修改状态
        var result = await mediator.Send(new ChangeFullReductionCampaignStatusCommand
        {
            OperationStoreId = operationStoreId,
            CampaignId = StoreApiHelpers.ParseRequiredSnowflake(request.ActivityId, nameof(request.ActivityId)),
            Status = request.Status
        }, cancellationToken);

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

    /// <summary>
    /// 删除满减活动。
    /// </summary>
    [HttpPost("delete")]
    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<object>> Delete(
        [FromBody] DeleteFullReductionRequest request,
        CancellationToken cancellationToken)
    {
        // 1. 校验操作门店
        var operationStoreId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(operationStoreId, cancellationToken);

        // 2. 调用应用层删除
        await mediator.Send(new DeleteFullReductionCampaignCommand
        {
            OperationStoreId = operationStoreId,
            CampaignId = StoreApiHelpers.ParseRequiredSnowflake(request.ActivityId, nameof(request.ActivityId))
        }, cancellationToken);

        return ApiResponse<object>.Ok(null);
    }

    private async Task<IReadOnlyCollection<long>> ResolveVisibleStoreIdsAsync(
        string? storeId,
        CancellationToken cancellationToken)
    {
        var (tenantId, merchantId) = StoreApiHelpers.GetTenantMerchantContext(storeContextService);

        // 1. 指定门店：返回单门店
        if (!string.IsNullOrWhiteSpace(storeId))
        {
            var parsedStoreId = StoreApiHelpers.ParseRequiredSnowflake(storeId, nameof(storeId));
            await StoreApiHelpers.EnsureStoreAccessibleAsync(dbContext, tenantId, merchantId, parsedStoreId, cancellationToken);
            return [parsedStoreId];
        }

        // 2. 全部门店：返回当前商户全部可见门店
        var allStoreIds = await dbContext.Stores
            .AsNoTracking()
            .Where(x => x.TenantId == tenantId && x.MerchantId == merchantId)
            .Select(x => x.Id)
            .OrderBy(x => x)
            .ToListAsync(cancellationToken);

        if (allStoreIds.Count == 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "当前商户下不存在可用门店");
        }

        return allStoreIds;
    }

    private async Task<IReadOnlyCollection<long>> ResolveStoreScopeStoreIdsAsync(
        string? storeScopeMode,
        IEnumerable<string>? storeIds,
        CancellationToken cancellationToken)
    {
        var (tenantId, merchantId) = StoreApiHelpers.GetTenantMerchantContext(storeContextService);
        var normalizedMode = NormalizeStoreScopeMode(storeScopeMode);

        // 1. all 模式展开为商户全部门店
        if (string.Equals(normalizedMode, "all", StringComparison.Ordinal))
        {
            var allStoreIds = await dbContext.Stores
                .AsNoTracking()
                .Where(x => x.TenantId == tenantId && x.MerchantId == merchantId)
                .Select(x => x.Id)
                .OrderBy(x => x)
                .ToListAsync(cancellationToken);

            if (allStoreIds.Count == 0)
            {
                throw new BusinessException(ErrorCodes.BadRequest, "当前商户下不存在可用门店");
            }

            return allStoreIds;
        }

        // 2. stores 模式校验传入门店
        var parsedStoreIds = StoreApiHelpers.ParseSnowflakeList(storeIds);
        if (parsedStoreIds.Count == 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "storeIds 不能为空");
        }

        var accessibleStoreIds = await StoreApiHelpers.FilterAccessibleStoreIdsAsync(
            dbContext,
            tenantId,
            merchantId,
            parsedStoreIds,
            cancellationToken);

        if (accessibleStoreIds.Count != parsedStoreIds.Count)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "storeIds 存在无权限门店");
        }

        return accessibleStoreIds.OrderBy(x => x).ToList();
    }

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

    private static string NormalizeStoreScopeMode(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        if (normalized is not ("all" or "stores"))
        {
            throw new BusinessException(ErrorCodes.BadRequest, "storeScopeMode 参数不合法");
        }

        return normalized;
    }

    private static FullReductionGiftRuleDto? MapGiftRuleRequest(FullReductionGiftRuleRequest? request)
    {
        if (request is null)
        {
            return null;
        }

        return new FullReductionGiftRuleDto
        {
            BuyQuantity = request.BuyQuantity,
            GiftQuantity = request.GiftQuantity,
            GiftScopeType = request.GiftScopeType,
            ApplicableScope = MapScopeRequest(request.ApplicableScope),
            GiftScope = MapScopeRequest(request.GiftScope)
        };
    }

    private static FullReductionSecondHalfRuleDto? MapSecondHalfRuleRequest(FullReductionSecondHalfRuleRequest? request)
    {
        if (request is null)
        {
            return null;
        }

        return new FullReductionSecondHalfRuleDto
        {
            DiscountType = request.DiscountType,
            ApplicableScope = MapScopeRequest(request.ApplicableScope)
        };
    }

    private static FullReductionScopeRuleDto MapScopeRequest(FullReductionScopeRuleRequest request)
    {
        return new FullReductionScopeRuleDto
        {
            ScopeType = request.ScopeType,
            CategoryIds = StoreApiHelpers.ParseSnowflakeList(request.CategoryIds),
            ProductIds = StoreApiHelpers.ParseSnowflakeList(request.ProductIds)
        };
    }

    private static FullReductionMetricsDto? MapMetricsRequest(FullReductionMetricsRequest? request)
    {
        if (request is null)
        {
            return null;
        }

        return new FullReductionMetricsDto
        {
            ParticipatingOrderCount = request.ParticipatingOrderCount,
            DiscountTotalAmount = request.DiscountTotalAmount,
            TicketIncreaseAmount = request.TicketIncreaseAmount,
            GiftedCount = request.GiftedCount,
            DrivenSalesAmount = request.DrivenSalesAmount,
            AttachRateIncreasePercent = request.AttachRateIncreasePercent,
            MonthlyDrivenSalesAmount = request.MonthlyDrivenSalesAmount,
            AverageTicketIncrease = request.AverageTicketIncrease
        };
    }

    private static FullReductionListItemResponse MapListItem(FullReductionListItemDto source)
    {
        return new FullReductionListItemResponse
        {
            Id = source.Id.ToString(),
            Name = source.Name,
            ActivityType = source.Rules.ActivityType,
            StartDate = ToDateOnly(source.StartAt),
            EndDate = ToDateOnly(source.EndAt),
            DisplayStatus = source.DisplayStatus,
            IsDimmed = source.IsDimmed,
            ReduceTiers = source.Rules.ReduceTiers.Select(MapTier).ToList(),
            GiftRule = source.Rules.GiftRule is null ? null : MapGiftRule(source.Rules.GiftRule),
            SecondHalfRule = source.Rules.SecondHalfRule is null
                ? null
                : MapSecondHalfRule(source.Rules.SecondHalfRule),
            Channels = source.Rules.Channels.ToList(),
            StoreScopeMode = source.Rules.StoreScopeMode,
            StoreIds = source.Rules.StoreIds.Select(item => item.ToString()).ToList(),
            ScopeStoreId = source.Rules.ScopeStoreId.ToString(),
            StackWithCoupon = source.Rules.StackWithCoupon,
            Description = source.Rules.Description,
            Metrics = MapMetrics(source.Rules.Metrics),
            UpdatedAt = ToDateTime(source.UpdatedAt)
        };
    }

    private static FullReductionDetailResponse MapDetail(FullReductionDetailDto source)
    {
        return new FullReductionDetailResponse
        {
            Id = source.Id.ToString(),
            Name = source.Name,
            ActivityType = source.Rules.ActivityType,
            ReduceTiers = source.Rules.ReduceTiers.Select(MapTier).ToList(),
            GiftRule = source.Rules.GiftRule is null ? null : MapGiftRule(source.Rules.GiftRule),
            SecondHalfRule = source.Rules.SecondHalfRule is null
                ? null
                : MapSecondHalfRule(source.Rules.SecondHalfRule),
            StartDate = ToDateOnly(source.StartAt),
            EndDate = ToDateOnly(source.EndAt),
            DisplayStatus = source.DisplayStatus,
            Status = source.Status,
            Channels = source.Rules.Channels.ToList(),
            StoreScopeMode = source.Rules.StoreScopeMode,
            StoreIds = source.Rules.StoreIds.Select(item => item.ToString()).ToList(),
            ScopeStoreId = source.Rules.ScopeStoreId.ToString(),
            StackWithCoupon = source.Rules.StackWithCoupon,
            Description = source.Rules.Description,
            Metrics = MapMetrics(source.Rules.Metrics),
            UpdatedAt = ToDateTime(source.UpdatedAt)
        };
    }

    private static FullReductionTierRuleResponse MapTier(FullReductionTierRuleDto source)
    {
        return new FullReductionTierRuleResponse
        {
            MeetAmount = source.MeetAmount,
            ReduceAmount = source.ReduceAmount
        };
    }

    private static FullReductionGiftRuleResponse MapGiftRule(FullReductionGiftRuleDto source)
    {
        return new FullReductionGiftRuleResponse
        {
            BuyQuantity = source.BuyQuantity,
            GiftQuantity = source.GiftQuantity,
            GiftScopeType = source.GiftScopeType,
            ApplicableScope = MapScope(source.ApplicableScope),
            GiftScope = MapScope(source.GiftScope)
        };
    }

    private static FullReductionSecondHalfRuleResponse MapSecondHalfRule(FullReductionSecondHalfRuleDto source)
    {
        return new FullReductionSecondHalfRuleResponse
        {
            DiscountType = source.DiscountType,
            ApplicableScope = MapScope(source.ApplicableScope)
        };
    }

    private static FullReductionScopeRuleResponse MapScope(FullReductionScopeRuleDto source)
    {
        return new FullReductionScopeRuleResponse
        {
            ScopeType = source.ScopeType,
            CategoryIds = source.CategoryIds.Select(item => item.ToString()).ToList(),
            ProductIds = source.ProductIds.Select(item => item.ToString()).ToList()
        };
    }

    private static FullReductionMetricsResponse MapMetrics(FullReductionMetricsDto source)
    {
        return new FullReductionMetricsResponse
        {
            ParticipatingOrderCount = source.ParticipatingOrderCount,
            DiscountTotalAmount = source.DiscountTotalAmount,
            TicketIncreaseAmount = source.TicketIncreaseAmount,
            GiftedCount = source.GiftedCount,
            DrivenSalesAmount = source.DrivenSalesAmount,
            AttachRateIncreasePercent = source.AttachRateIncreasePercent,
            MonthlyDrivenSalesAmount = source.MonthlyDrivenSalesAmount,
            AverageTicketIncrease = source.AverageTicketIncrease
        };
    }

    private static string ToDateOnly(DateTime value)
    {
        return value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
    }

    private static string ToDateTime(DateTime value)
    {
        return value.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
    }
}
