using System.Globalization;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TakeoutSaaS.Application.App.Coupons.Seckill.Commands;
using TakeoutSaaS.Application.App.Coupons.Seckill.Dto;
using TakeoutSaaS.Application.App.Coupons.Seckill.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/seckill")]
public sealed class MarketingSeckillController(
    IMediator mediator,
    TakeoutAppDbContext dbContext,
    StoreContextService storeContextService) : BaseApiController
{
    /// <summary>
    /// 获取秒杀活动列表。
    /// </summary>
    [HttpGet("list")]
    [ProducesResponseType(typeof(ApiResponse<SeckillListResultResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<SeckillListResultResponse>> List(
        [FromQuery] SeckillListRequest request,
        CancellationToken cancellationToken)
    {
        var visibleStoreIds = await ResolveVisibleStoreIdsAsync(request.StoreId, cancellationToken);

        var result = await mediator.Send(new GetSeckillCampaignListQuery
        {
            VisibleStoreIds = visibleStoreIds,
            Keyword = request.Keyword,
            Status = request.Status,
            Page = request.Page,
            PageSize = request.PageSize
        }, cancellationToken);

        return ApiResponse<SeckillListResultResponse>.Ok(new SeckillListResultResponse
        {
            Items = result.Items.Select(MapListItem).ToList(),
            Total = result.TotalCount,
            Page = result.Page,
            PageSize = result.PageSize,
            Stats = new SeckillStatsResponse
            {
                TotalCount = result.Stats.TotalCount,
                OngoingCount = result.Stats.OngoingCount,
                MonthlySeckillSalesCount = result.Stats.MonthlySeckillSalesCount,
                ConversionRate = result.Stats.ConversionRate
            }
        });
    }

    /// <summary>
    /// 获取秒杀活动详情。
    /// </summary>
    [HttpGet("detail")]
    [ProducesResponseType(typeof(ApiResponse<SeckillDetailResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<SeckillDetailResponse>> Detail(
        [FromQuery] SeckillDetailRequest request,
        CancellationToken cancellationToken)
    {
        var operationStoreId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(operationStoreId, cancellationToken);

        var result = await mediator.Send(new GetSeckillCampaignDetailQuery
        {
            OperationStoreId = operationStoreId,
            CampaignId = StoreApiHelpers.ParseRequiredSnowflake(request.ActivityId, nameof(request.ActivityId))
        }, cancellationToken);

        if (result is null)
        {
            return ApiResponse<SeckillDetailResponse>.Error(ErrorCodes.NotFound, "活动不存在");
        }

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

    /// <summary>
    /// 保存秒杀活动（新增/编辑）。
    /// </summary>
    [HttpPost("save")]
    [ProducesResponseType(typeof(ApiResponse<SeckillDetailResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<SeckillDetailResponse>> Save(
        [FromBody] SaveSeckillRequest request,
        CancellationToken cancellationToken)
    {
        var operationStoreId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(operationStoreId, cancellationToken);

        var resolvedStoreIds = await ResolveStoreIdsForSaveAsync(
            request.StoreIds,
            operationStoreId,
            cancellationToken);

        var result = await mediator.Send(new SaveSeckillCampaignCommand
        {
            OperationStoreId = operationStoreId,
            CampaignId = StoreApiHelpers.ParseSnowflakeOrNull(request.Id),
            Name = request.Name,
            ActivityType = request.ActivityType,
            StartDate = ParseDateOnlyOrNull(request.StartDate, nameof(request.StartDate)),
            EndDate = ParseDateOnlyOrNull(request.EndDate, nameof(request.EndDate)),
            TimeStart = ParseTimeOrNull(request.TimeStart, nameof(request.TimeStart)),
            TimeEnd = ParseTimeOrNull(request.TimeEnd, nameof(request.TimeEnd)),
            Sessions = (request.Sessions ?? [])
                .Select(item => new SeckillSessionRuleDto
                {
                    StartTime = StoreApiHelpers.ParseRequiredTime(item.StartTime, "sessions.startTime"),
                    DurationMinutes = item.DurationMinutes
                })
                .ToList(),
            Channels = request.Channels ?? [],
            PerUserLimit = request.PerUserLimit,
            PreheatEnabled = request.PreheatEnabled,
            PreheatHours = request.PreheatHours,
            StoreIds = resolvedStoreIds,
            Products = request.Products.Select(item => new SeckillSaveProductInputDto
            {
                ProductId = StoreApiHelpers.ParseRequiredSnowflake(item.ProductId, nameof(item.ProductId)),
                SeckillPrice = item.SeckillPrice,
                StockLimit = item.StockLimit,
                PerUserLimit = item.PerUserLimit
            }).ToList(),
            Metrics = request.Metrics is null
                ? null
                : new SeckillMetricsDto
                {
                    ParticipantCount = request.Metrics.ParticipantCount,
                    DealCount = request.Metrics.DealCount,
                    ConversionRate = request.Metrics.ConversionRate,
                    MonthlySeckillSalesCount = request.Metrics.MonthlySeckillSalesCount
                }
        }, cancellationToken);

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

    /// <summary>
    /// 修改秒杀活动状态。
    /// </summary>
    [HttpPost("status")]
    [ProducesResponseType(typeof(ApiResponse<SeckillDetailResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<SeckillDetailResponse>> ChangeStatus(
        [FromBody] ChangeSeckillStatusRequest request,
        CancellationToken cancellationToken)
    {
        var operationStoreId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(operationStoreId, cancellationToken);

        var result = await mediator.Send(new ChangeSeckillCampaignStatusCommand
        {
            OperationStoreId = operationStoreId,
            CampaignId = StoreApiHelpers.ParseRequiredSnowflake(request.ActivityId, nameof(request.ActivityId)),
            Status = request.Status
        }, cancellationToken);

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

    /// <summary>
    /// 删除秒杀活动。
    /// </summary>
    [HttpPost("delete")]
    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<object>> Delete(
        [FromBody] DeleteSeckillRequest request,
        CancellationToken cancellationToken)
    {
        var operationStoreId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(operationStoreId, cancellationToken);

        await mediator.Send(new DeleteSeckillCampaignCommand
        {
            OperationStoreId = operationStoreId,
            CampaignId = StoreApiHelpers.ParseRequiredSnowflake(request.ActivityId, nameof(request.ActivityId))
        }, cancellationToken);

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

    /// <summary>
    /// 获取秒杀选品分类。
    /// </summary>
    [HttpGet("picker/categories")]
    [ProducesResponseType(typeof(ApiResponse<List<SeckillPickerCategoryItemResponse>>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<List<SeckillPickerCategoryItemResponse>>> PickerCategories(
        [FromQuery] SeckillPickerCategoriesRequest request,
        CancellationToken cancellationToken)
    {
        var operationStoreId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(operationStoreId, cancellationToken);

        var result = await mediator.Send(new GetSeckillPickerCategoriesQuery
        {
            OperationStoreId = operationStoreId
        }, cancellationToken);

        return ApiResponse<List<SeckillPickerCategoryItemResponse>>.Ok(result
            .Select(item => new SeckillPickerCategoryItemResponse
            {
                Id = item.Id.ToString(),
                Name = item.Name,
                ProductCount = item.ProductCount
            })
            .ToList());
    }

    /// <summary>
    /// 获取秒杀选品商品。
    /// </summary>
    [HttpGet("picker/products")]
    [ProducesResponseType(typeof(ApiResponse<List<SeckillPickerProductItemResponse>>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<List<SeckillPickerProductItemResponse>>> PickerProducts(
        [FromQuery] SeckillPickerProductsRequest request,
        CancellationToken cancellationToken)
    {
        var operationStoreId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(operationStoreId, cancellationToken);

        var result = await mediator.Send(new GetSeckillPickerProductsQuery
        {
            OperationStoreId = operationStoreId,
            CategoryId = StoreApiHelpers.ParseSnowflakeOrNull(request.CategoryId),
            Keyword = request.Keyword,
            Limit = request.Limit
        }, cancellationToken);

        return ApiResponse<List<SeckillPickerProductItemResponse>>.Ok(result
            .Select(item => new SeckillPickerProductItemResponse
            {
                Id = item.Id.ToString(),
                CategoryId = item.CategoryId.ToString(),
                CategoryName = item.CategoryName,
                Name = item.Name,
                Price = item.Price,
                Stock = item.Stock,
                SpuCode = item.SpuCode,
                Status = item.Status
            })
            .ToList());
    }

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

        if (!string.IsNullOrWhiteSpace(storeId))
        {
            var parsedStoreId = StoreApiHelpers.ParseRequiredSnowflake(storeId, nameof(storeId));
            await StoreApiHelpers.EnsureStoreAccessibleAsync(dbContext, tenantId, merchantId, parsedStoreId, cancellationToken);
            return [parsedStoreId];
        }

        var allStoreIds = await dbContext.Stores
            .AsNoTracking()
            .Where(item => item.TenantId == tenantId && item.MerchantId == merchantId)
            .Select(item => item.Id)
            .OrderBy(item => item)
            .ToListAsync(cancellationToken);

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

        return allStoreIds;
    }

    private async Task<IReadOnlyCollection<long>> ResolveStoreIdsForSaveAsync(
        IEnumerable<string>? storeIds,
        long operationStoreId,
        CancellationToken cancellationToken)
    {
        var parsedStoreIds = StoreApiHelpers.ParseSnowflakeList(storeIds);
        if (parsedStoreIds.Count == 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "storeIds 不能为空");
        }

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

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

        if (!accessibleStoreIds.Contains(operationStoreId))
        {
            throw new BusinessException(ErrorCodes.BadRequest, "storeIds 必须包含当前操作门店");
        }

        return accessibleStoreIds.OrderBy(item => item).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 DateTime? ParseDateOnlyOrNull(string? value, string fieldName)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return null;
        }

        if (!DateTime.TryParseExact(value, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var parsed))
        {
            throw new BusinessException(ErrorCodes.BadRequest, $"{fieldName} 日期格式必须为 yyyy-MM-dd");
        }

        return DateTime.SpecifyKind(parsed.Date, DateTimeKind.Utc);
    }

    private static TimeSpan? ParseTimeOrNull(string? value, string fieldName)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return null;
        }

        return StoreApiHelpers.ParseRequiredTime(value, fieldName);
    }

    private static SeckillListItemResponse MapListItem(SeckillListItemDto source)
    {
        return new SeckillListItemResponse
        {
            Id = source.Id.ToString(),
            Name = source.Name,
            ActivityType = source.Rules.ActivityType,
            StartDate = ToDateOnly(source.Rules.StartDate),
            EndDate = ToDateOnly(source.Rules.EndDate),
            TimeStart = StoreApiHelpers.ToHHmm(source.Rules.TimeStart),
            TimeEnd = StoreApiHelpers.ToHHmm(source.Rules.TimeEnd),
            Sessions = source.Rules.Sessions.Select(MapSession).ToList(),
            Status = source.Status,
            DisplayStatus = source.DisplayStatus,
            IsDimmed = source.IsDimmed,
            Channels = source.Rules.Channels.ToList(),
            PerUserLimit = source.Rules.PerUserLimit,
            PreheatEnabled = source.Rules.PreheatEnabled,
            PreheatHours = source.Rules.PreheatHours,
            StoreIds = source.Rules.StoreIds.Select(item => item.ToString()).ToList(),
            Products = source.Rules.Products.Select(MapProduct).ToList(),
            Metrics = MapMetrics(source.Rules.Metrics),
            UpdatedAt = ToDateTime(source.UpdatedAt)
        };
    }

    private static SeckillDetailResponse MapDetail(SeckillDetailDto source)
    {
        return new SeckillDetailResponse
        {
            Id = source.Id.ToString(),
            Name = source.Name,
            ActivityType = source.Rules.ActivityType,
            StartDate = ToDateOnly(source.Rules.StartDate),
            EndDate = ToDateOnly(source.Rules.EndDate),
            TimeStart = StoreApiHelpers.ToHHmm(source.Rules.TimeStart),
            TimeEnd = StoreApiHelpers.ToHHmm(source.Rules.TimeEnd),
            Sessions = source.Rules.Sessions.Select(MapSession).ToList(),
            Status = source.Status,
            DisplayStatus = source.DisplayStatus,
            Channels = source.Rules.Channels.ToList(),
            PerUserLimit = source.Rules.PerUserLimit,
            PreheatEnabled = source.Rules.PreheatEnabled,
            PreheatHours = source.Rules.PreheatHours,
            StoreIds = source.Rules.StoreIds.Select(item => item.ToString()).ToList(),
            Products = source.Rules.Products.Select(MapProduct).ToList(),
            Metrics = MapMetrics(source.Rules.Metrics),
            UpdatedAt = ToDateTime(source.UpdatedAt)
        };
    }

    private static SeckillSessionResponse MapSession(SeckillSessionRuleDto source)
    {
        return new SeckillSessionResponse
        {
            StartTime = StoreApiHelpers.ToHHmm(source.StartTime),
            DurationMinutes = source.DurationMinutes
        };
    }

    private static SeckillProductResponse MapProduct(SeckillProductRuleDto source)
    {
        return new SeckillProductResponse
        {
            ProductId = source.ProductId.ToString(),
            CategoryId = source.CategoryId.ToString(),
            CategoryName = source.CategoryName,
            Name = source.Name,
            SpuCode = source.SpuCode,
            Status = source.Status,
            OriginalPrice = source.OriginalPrice,
            SeckillPrice = source.SeckillPrice,
            StockLimit = source.StockLimit,
            PerUserLimit = source.PerUserLimit,
            SoldCount = source.SoldCount
        };
    }

    private static SeckillMetricsResponse MapMetrics(SeckillMetricsDto source)
    {
        return new SeckillMetricsResponse
        {
            ParticipantCount = source.ParticipantCount,
            DealCount = source.DealCount,
            ConversionRate = source.ConversionRate,
            MonthlySeckillSalesCount = source.MonthlySeckillSalesCount
        };
    }

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