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

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

        return ApiResponse<FlashSaleListResultResponse>.Ok(new FlashSaleListResultResponse
        {
            Items = result.Items.Select(MapListItem).ToList(),
            Total = result.TotalCount,
            Page = result.Page,
            PageSize = result.PageSize,
            Stats = new FlashSaleStatsResponse
            {
                TotalCount = result.Stats.TotalCount,
                OngoingCount = result.Stats.OngoingCount,
                ParticipatingProductCount = result.Stats.ParticipatingProductCount,
                MonthlyDiscountSalesCount = result.Stats.MonthlyDiscountSalesCount
            }
        });
    }

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

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

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

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

    /// <summary>
    /// 保存限时折扣活动（新增/编辑）。
    /// </summary>
    [HttpPost("save")]
    [ProducesResponseType(typeof(ApiResponse<FlashSaleDetailResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<FlashSaleDetailResponse>> Save(
        [FromBody] SaveFlashSaleRequest 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 SaveFlashSaleCampaignCommand
        {
            OperationStoreId = operationStoreId,
            CampaignId = StoreApiHelpers.ParseSnowflakeOrNull(request.Id),
            Name = request.Name,
            CycleType = request.CycleType,
            RecurringDateMode = request.RecurringDateMode,
            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)),
            WeekDays = request.WeekDays ?? [],
            Channels = request.Channels ?? [],
            PerUserLimit = request.PerUserLimit,
            StoreIds = resolvedStoreIds,
            Products = request.Products.Select(item => new FlashSaleSaveProductInputDto
            {
                ProductId = StoreApiHelpers.ParseRequiredSnowflake(item.ProductId, nameof(item.ProductId)),
                DiscountPrice = item.DiscountPrice,
                PerUserLimit = item.PerUserLimit
            }).ToList(),
            Metrics = request.Metrics is null
                ? null
                : new FlashSaleMetricsDto
                {
                    ActivitySalesCount = request.Metrics.ActivitySalesCount,
                    DiscountTotalAmount = request.Metrics.DiscountTotalAmount,
                    LoopedWeeks = request.Metrics.LoopedWeeks,
                    MonthlyDiscountSalesCount = request.Metrics.MonthlyDiscountSalesCount
                }
        }, cancellationToken);

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

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

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

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

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

        await mediator.Send(new DeleteFlashSaleCampaignCommand
        {
            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<FlashSalePickerCategoryItemResponse>>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<List<FlashSalePickerCategoryItemResponse>>> PickerCategories(
        [FromQuery] FlashSalePickerCategoriesRequest request,
        CancellationToken cancellationToken)
    {
        var operationStoreId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(operationStoreId, cancellationToken);

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

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

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

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

        return ApiResponse<List<FlashSalePickerProductItemResponse>>.Ok(result
            .Select(item => new FlashSalePickerProductItemResponse
            {
                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 FlashSaleListItemResponse MapListItem(FlashSaleListItemDto source)
    {
        return new FlashSaleListItemResponse
        {
            Id = source.Id.ToString(),
            Name = source.Name,
            CycleType = source.Rules.CycleType,
            RecurringDateMode = source.Rules.RecurringDateMode,
            StartDate = ToDateOnly(source.Rules.StartDate),
            EndDate = ToDateOnly(source.Rules.EndDate),
            TimeStart = StoreApiHelpers.ToHHmm(source.Rules.TimeStart),
            TimeEnd = StoreApiHelpers.ToHHmm(source.Rules.TimeEnd),
            WeekDays = source.Rules.WeekDays.ToList(),
            Status = source.Status,
            DisplayStatus = source.DisplayStatus,
            IsDimmed = source.IsDimmed,
            Channels = source.Rules.Channels.ToList(),
            PerUserLimit = source.Rules.PerUserLimit,
            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 FlashSaleDetailResponse MapDetail(FlashSaleDetailDto source)
    {
        return new FlashSaleDetailResponse
        {
            Id = source.Id.ToString(),
            Name = source.Name,
            CycleType = source.Rules.CycleType,
            RecurringDateMode = source.Rules.RecurringDateMode,
            StartDate = ToDateOnly(source.Rules.StartDate),
            EndDate = ToDateOnly(source.Rules.EndDate),
            TimeStart = StoreApiHelpers.ToHHmm(source.Rules.TimeStart),
            TimeEnd = StoreApiHelpers.ToHHmm(source.Rules.TimeEnd),
            WeekDays = source.Rules.WeekDays.ToList(),
            Status = source.Status,
            DisplayStatus = source.DisplayStatus,
            Channels = source.Rules.Channels.ToList(),
            PerUserLimit = source.Rules.PerUserLimit,
            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 FlashSaleProductResponse MapProduct(FlashSaleProductRuleDto source)
    {
        return new FlashSaleProductResponse
        {
            ProductId = source.ProductId.ToString(),
            CategoryId = source.CategoryId.ToString(),
            CategoryName = source.CategoryName,
            Name = source.Name,
            SpuCode = source.SpuCode,
            Status = source.Status,
            OriginalPrice = source.OriginalPrice,
            DiscountPrice = source.DiscountPrice,
            PerUserLimit = source.PerUserLimit,
            SoldCount = source.SoldCount
        };
    }

    private static FlashSaleMetricsResponse MapMetrics(FlashSaleMetricsDto source)
    {
        return new FlashSaleMetricsResponse
        {
            ActivitySalesCount = source.ActivitySalesCount,
            DiscountTotalAmount = source.DiscountTotalAmount,
            LoopedWeeks = source.LoopedWeeks,
            MonthlyDiscountSalesCount = source.MonthlyDiscountSalesCount
        };
    }

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

