using MediatR;
using TakeoutSaaS.Application.App.Products.Commands;
using TakeoutSaaS.Application.App.Products.Dto;
using TakeoutSaaS.Domain.Products.Entities;
using TakeoutSaaS.Domain.Products.Enums;
using TakeoutSaaS.Domain.Products.Repositories;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
using TakeoutSaaS.Shared.Abstractions.Tenancy;

namespace TakeoutSaaS.Application.App.Products.Handlers;

/// <summary>
/// 保存加料组命令处理器。
/// </summary>
public sealed class SaveProductAddonGroupCommandHandler(
    IProductRepository productRepository,
    ITenantProvider tenantProvider)
    : IRequestHandler<SaveProductAddonGroupCommand, ProductAddonTemplateItemDto>
{
    /// <inheritdoc />
    public async Task<ProductAddonTemplateItemDto> Handle(SaveProductAddonGroupCommand request, CancellationToken cancellationToken)
    {
        // 1. 校验基础输入并归一化参数。
        var tenantId = tenantProvider.GetCurrentTenantId();
        var normalizedName = request.Name.Trim();
        if (string.IsNullOrWhiteSpace(normalizedName))
        {
            throw new BusinessException(ErrorCodes.BadRequest, "加料组名称不能为空");
        }

        if (!ProductAddonTemplateMapping.TryParseStatus(request.Status, out var isEnabled))
        {
            throw new BusinessException(ErrorCodes.BadRequest, "status 参数不合法");
        }

        var normalizedDescription = request.Description.Trim();
        var normalizedMinSelect = Math.Max(0, request.MinSelect);
        var normalizedMaxSelect = Math.Max(1, request.MaxSelect);
        if (normalizedMaxSelect < normalizedMinSelect)
        {
            normalizedMaxSelect = normalizedMinSelect;
        }

        if (request.Required && normalizedMinSelect == 0)
        {
            normalizedMinSelect = 1;
            if (normalizedMaxSelect < 1)
            {
                normalizedMaxSelect = 1;
            }
        }

        var normalizedItems = NormalizeItems(request.Items);
        if (normalizedItems.Count == 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "请至少添加一个加料项");
        }

        // 2. 校验同门店名称唯一。
        var isDuplicate = await productRepository.ExistsAddonTemplateNameAsync(
            tenantId,
            request.StoreId,
            normalizedName,
            request.GroupId,
            cancellationToken);
        if (isDuplicate)
        {
            throw new BusinessException(ErrorCodes.Conflict, "加料组名称已存在");
        }

        // 3. 过滤有效商品并创建或更新模板主记录。
        var normalizedProductIds = request.ProductIds
            .Where(id => id > 0)
            .Distinct()
            .ToList();
        var validProductIds = await productRepository.FilterExistingProductIdsAsync(
            tenantId,
            request.StoreId,
            normalizedProductIds,
            cancellationToken);

        ProductSpecTemplate template;
        var isCreate = !request.GroupId.HasValue;
        if (isCreate)
        {
            template = new ProductSpecTemplate
            {
                StoreId = request.StoreId,
                Name = normalizedName,
                Description = normalizedDescription,
                TemplateType = ProductSpecTemplateType.Addon,
                SelectionType = ProductAddonTemplateMapping.ToSelectionType(normalizedMaxSelect),
                IsRequired = request.Required,
                MinSelect = normalizedMinSelect,
                MaxSelect = normalizedMaxSelect,
                SortOrder = Math.Max(1, request.Sort),
                IsEnabled = isEnabled
            };

            await productRepository.AddSpecTemplateAsync(template, cancellationToken);
            await productRepository.SaveChangesAsync(cancellationToken);
        }
        else
        {
            var groupId = request.GroupId
                ?? throw new BusinessException(ErrorCodes.BadRequest, "加料组标识不能为空");

            var existing = await productRepository.FindAddonTemplateByIdAsync(
                groupId,
                tenantId,
                cancellationToken);
            if (existing is null || existing.StoreId != request.StoreId)
            {
                throw new BusinessException(ErrorCodes.NotFound, "加料组不存在");
            }

            template = existing;
            template.Name = normalizedName;
            template.Description = normalizedDescription;
            template.SelectionType = ProductAddonTemplateMapping.ToSelectionType(normalizedMaxSelect);
            template.IsRequired = request.Required;
            template.MinSelect = normalizedMinSelect;
            template.MaxSelect = normalizedMaxSelect;
            template.SortOrder = Math.Max(1, request.Sort);
            template.IsEnabled = isEnabled;

            await productRepository.UpdateSpecTemplateAsync(template, cancellationToken);
        }

        // 4. 替换选项与关联商品。
        await productRepository.RemoveSpecTemplateOptionsAsync(template.Id, tenantId, cancellationToken);
        await productRepository.RemoveSpecTemplateProductsAsync(template.Id, tenantId, request.StoreId, cancellationToken);

        var optionEntities = normalizedItems
            .Select(item => new ProductSpecTemplateOption
            {
                TemplateId = template.Id,
                Name = item.Name,
                ExtraPrice = item.Price,
                Stock = item.Stock,
                IsEnabled = item.IsEnabled,
                SortOrder = item.Sort
            })
            .ToList();
        if (optionEntities.Count > 0)
        {
            await productRepository.AddSpecTemplateOptionsAsync(optionEntities, cancellationToken);
        }

        var relationEntities = validProductIds
            .Select(productId => new ProductSpecTemplateProduct
            {
                StoreId = request.StoreId,
                TemplateId = template.Id,
                ProductId = productId
            })
            .ToList();
        if (relationEntities.Count > 0)
        {
            await productRepository.AddSpecTemplateProductsAsync(relationEntities, cancellationToken);
        }

        await productRepository.SaveChangesAsync(cancellationToken);

        // 5. 回读并返回最新快照。
        return await LoadAddonTemplateSnapshotAsync(template, tenantId, request.StoreId, cancellationToken);
    }

    private static IReadOnlyList<NormalizedAddonItem> NormalizeItems(IReadOnlyList<SaveProductAddonItemCommand>? items)
    {
        var source = items ?? [];
        var normalized = source
            .Select((item, index) =>
            {
                if (!ProductAddonTemplateMapping.TryParseStatus(item.Status, out var isEnabled))
                {
                    throw new BusinessException(ErrorCodes.BadRequest, "加料项状态不合法");
                }

                return new NormalizedAddonItem
                {
                    Name = item.Name.Trim(),
                    Price = item.Price,
                    Stock = Math.Max(0, item.Stock),
                    Sort = item.Sort > 0 ? item.Sort : index + 1,
                    IsEnabled = isEnabled
                };
            })
            .Where(item => !string.IsNullOrWhiteSpace(item.Name))
            .OrderBy(item => item.Sort)
            .ToList();

        var duplicate = normalized
            .GroupBy(item => item.Name, StringComparer.OrdinalIgnoreCase)
            .Any(group => group.Count() > 1);
        if (duplicate)
        {
            throw new BusinessException(ErrorCodes.Conflict, "加料项名称重复");
        }

        for (var index = 0; index < normalized.Count; index++)
        {
            normalized[index].Sort = index + 1;
        }

        return normalized;
    }

    private async Task<ProductAddonTemplateItemDto> LoadAddonTemplateSnapshotAsync(
        ProductSpecTemplate template,
        long tenantId,
        long storeId,
        CancellationToken cancellationToken)
    {
        var options = await productRepository.GetSpecTemplateOptionsByTemplateIdsAsync([template.Id], tenantId, cancellationToken);
        var relations = await productRepository.GetSpecTemplateProductsByTemplateIdsAsync([template.Id], tenantId, storeId, cancellationToken);

        var optionsLookup = options
            .GroupBy(x => x.TemplateId)
            .ToDictionary(group => group.Key, group => group.OrderBy(item => item.SortOrder).ThenBy(item => item.Id).ToList());
        var productIdsLookup = relations
            .GroupBy(x => x.TemplateId)
            .ToDictionary(group => group.Key, group => group.Select(item => item.ProductId).ToList());

        return ProductAddonTemplateDtoFactory.ToDto(template, optionsLookup, productIdsLookup);
    }

    private sealed class NormalizedAddonItem
    {
        public bool IsEnabled { get; init; }

        public string Name { get; init; } = string.Empty;

        public decimal Price { get; init; }

        public int Sort { get; set; }

        public int Stock { get; init; }
    }
}
