using System.Globalization;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TakeoutSaaS.Application.App.Coupons.Commands;
using TakeoutSaaS.Application.App.Coupons.Dto;
using TakeoutSaaS.Application.App.Coupons.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/coupon")]
public sealed class MarketingCouponController(
    IMediator mediator,
    TakeoutAppDbContext dbContext,
    StoreContextService storeContextService) : BaseApiController
{
    /// <summary>
    /// 获取优惠券列表。
    /// </summary>
    [HttpGet("list")]
    [ProducesResponseType(typeof(ApiResponse<CouponListResultResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<CouponListResultResponse>> List(
        [FromQuery] CouponListRequest request,
        CancellationToken cancellationToken)
    {
        // 1. 解析并校验当前门店上下文
        var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(storeId, cancellationToken);

        // 2. 调用应用层查询
        var result = await mediator.Send(new GetCouponTemplateListQuery
        {
            StoreId = storeId,
            Keyword = request.Keyword,
            Status = request.Status,
            CouponType = request.CouponType,
            Page = request.Page,
            PageSize = request.PageSize
        }, cancellationToken);

        // 3. 映射响应
        return ApiResponse<CouponListResultResponse>.Ok(new CouponListResultResponse
        {
            Items = result.Items.Select(MapListItem).ToList(),
            Total = result.TotalCount,
            Page = result.Page,
            PageSize = result.PageSize,
            Stats = new CouponStatsResponse
            {
                TotalCount = result.Stats.TotalCount,
                OngoingCount = result.Stats.OngoingCount,
                ClaimedCount = result.Stats.ClaimedCount,
                RedeemedCount = result.Stats.RedeemedCount,
                RedeemRate = result.Stats.RedeemRate
            }
        });
    }

    /// <summary>
    /// 获取优惠券详情。
    /// </summary>
    [HttpGet("detail")]
    [ProducesResponseType(typeof(ApiResponse<CouponDetailResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<CouponDetailResponse>> Detail(
        [FromQuery] CouponDetailRequest request,
        CancellationToken cancellationToken)
    {
        // 1. 解析并校验门店访问权限
        var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(storeId, cancellationToken);

        // 2. 查询详情
        var result = await mediator.Send(new GetCouponTemplateDetailQuery
        {
            StoreId = storeId,
            TemplateId = StoreApiHelpers.ParseRequiredSnowflake(request.CouponId, nameof(request.CouponId))
        }, cancellationToken);

        // 3. 处理不存在场景
        if (result is null)
        {
            return ApiResponse<CouponDetailResponse>.Error(ErrorCodes.NotFound, "优惠券不存在");
        }

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

    /// <summary>
    /// 保存优惠券模板（新增/编辑）。
    /// </summary>
    [HttpPost("save")]
    [ProducesResponseType(typeof(ApiResponse<CouponDetailResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<CouponDetailResponse>> Save(
        [FromBody] SaveCouponRequest request,
        CancellationToken cancellationToken)
    {
        // 1. 解析并校验操作门店
        var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        var (tenantId, merchantId) = StoreApiHelpers.GetTenantMerchantContext(storeContextService);
        await StoreApiHelpers.EnsureStoreAccessibleAsync(dbContext, tenantId, merchantId, storeId, cancellationToken);

        // 2. 解析适用门店范围
        var resolvedStoreIds = await ResolveStoreScopeStoreIdsAsync(
            request.StoreScopeMode,
            request.StoreIds,
            tenantId,
            merchantId,
            cancellationToken);

        // 3. 调用应用层保存
        var result = await mediator.Send(new SaveCouponTemplateCommand
        {
            StoreId = storeId,
            TemplateId = StoreApiHelpers.ParseSnowflakeOrNull(request.Id),
            Name = request.Name,
            CouponType = request.CouponType,
            Value = request.Value,
            MinimumSpend = request.MinimumSpend,
            TotalQuantity = request.TotalQuantity,
            PerUserLimit = request.PerUserLimit,
            ValidityType = request.ValidityType,
            ValidFrom = request.ValidFrom,
            ValidTo = request.ValidTo,
            RelativeValidDays = request.RelativeValidDays,
            Channels = request.Channels ?? [],
            StoreScopeMode = NormalizeStoreScopeMode(request.StoreScopeMode),
            StoreScopeStoreIds = resolvedStoreIds,
            Status = request.Status
        }, cancellationToken);

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

    /// <summary>
    /// 修改优惠券状态。
    /// </summary>
    [HttpPost("status")]
    [ProducesResponseType(typeof(ApiResponse<CouponDetailResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<CouponDetailResponse>> ChangeStatus(
        [FromBody] ChangeCouponStatusRequest request,
        CancellationToken cancellationToken)
    {
        // 1. 解析并校验门店访问权限
        var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(storeId, cancellationToken);

        // 2. 调用应用层修改状态
        var result = await mediator.Send(new ChangeCouponTemplateStatusCommand
        {
            StoreId = storeId,
            TemplateId = StoreApiHelpers.ParseRequiredSnowflake(request.CouponId, nameof(request.CouponId)),
            Status = request.Status
        }, cancellationToken);

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

    /// <summary>
    /// 删除优惠券模板。
    /// </summary>
    [HttpPost("delete")]
    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<object>> Delete(
        [FromBody] DeleteCouponRequest request,
        CancellationToken cancellationToken)
    {
        // 1. 解析并校验门店访问权限
        var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(storeId, cancellationToken);

        // 2. 调用应用层删除
        await mediator.Send(new DeleteCouponTemplateCommand
        {
            StoreId = storeId,
            TemplateId = StoreApiHelpers.ParseRequiredSnowflake(request.CouponId, nameof(request.CouponId))
        }, cancellationToken);

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

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

    private async Task<IReadOnlyCollection<long>> ResolveStoreScopeStoreIdsAsync(
        string? storeScopeMode,
        IEnumerable<string>? storeIds,
        long tenantId,
        long merchantId,
        CancellationToken cancellationToken)
    {
        // 1. 标准化 mode
        var normalizedMode = NormalizeStoreScopeMode(storeScopeMode);

        // 2. 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;
        }

        // 3. 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 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 CouponListItemResponse MapListItem(CouponTemplateListItemDto source)
    {
        return new CouponListItemResponse
        {
            Id = source.Id.ToString(),
            Name = source.Name,
            CouponType = source.CouponType,
            Value = source.Value,
            MinimumSpend = source.MinimumSpend,
            ValidFrom = ToDateOnly(source.ValidFrom),
            ValidTo = ToDateOnly(source.ValidTo),
            RelativeValidDays = source.RelativeValidDays,
            TotalQuantity = source.TotalQuantity,
            ClaimedQuantity = source.ClaimedQuantity,
            RedeemedQuantity = source.RedeemedQuantity,
            PerUserLimit = source.PerUserLimit,
            DisplayStatus = source.DisplayStatus,
            IsDimmed = source.IsDimmed,
            StoreScopeMode = source.StoreScopeMode,
            StoreIds = source.StoreIds.Select(item => item.ToString()).ToList(),
            Channels = source.Channels.ToList(),
            UpdatedAt = ToDateTime(source.UpdatedAt)
        };
    }

    private static CouponDetailResponse MapDetail(CouponTemplateDetailDto source)
    {
        return new CouponDetailResponse
        {
            Id = source.Id.ToString(),
            Name = source.Name,
            CouponType = source.CouponType,
            Value = source.Value,
            MinimumSpend = source.MinimumSpend,
            TotalQuantity = source.TotalQuantity,
            ClaimedQuantity = source.ClaimedQuantity,
            PerUserLimit = source.PerUserLimit,
            ValidityType = source.ValidityType,
            ValidFrom = ToDateOnly(source.ValidFrom),
            ValidTo = ToDateOnly(source.ValidTo),
            RelativeValidDays = source.RelativeValidDays,
            Channels = source.Channels.ToList(),
            StoreScopeMode = source.StoreScopeMode,
            StoreIds = source.StoreIds.Select(item => item.ToString()).ToList(),
            Status = source.Status,
            UpdatedAt = ToDateTime(source.UpdatedAt)
        };
    }

    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);
    }
}
