using System.Security.Cryptography;
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Npgsql;
using TakeoutSaaS.Domain.Products.Entities;
using TakeoutSaaS.Domain.Products.Enums;
using TakeoutSaaS.Infrastructure.App.Persistence;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
using TakeoutSaaS.TenantApi.Controllers;

namespace TakeoutSaaS.TenantApi.Services;

/// <summary>
/// 商品 SKU 保存服务（replace 语义）。
/// </summary>
public sealed class ProductSkuSaveService(TakeoutAppDbContext dbContext)
{
    /// <summary>
    /// 按 replace 语义保存 SKU：
    /// 1. 命中的 SKU 更新并恢复启用。
    /// 2. 未命中的 SKU 新增。
    /// 3. 缺失的历史 SKU 软禁用（IsEnabled=false, Stock=0）。
    /// </summary>
    public async Task ReplaceSkusAsync(
        long productId,
        long storeId,
        IReadOnlyList<ProductSkuUpsertInput> skus,
        IReadOnlyCollection<long> specTemplateIds,
        CancellationToken cancellationToken)
    {
        var normalizedSkus = skus ?? [];
        if (normalizedSkus.Count == 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "skus 不能为空");
        }

        await ValidateSkuTemplateRefsAsync(storeId, normalizedSkus, specTemplateIds, cancellationToken);

        var explicitSkuCodes = normalizedSkus
            .Select(item => NormalizeSkuCode(item.SkuCode))
            .Where(item => !string.IsNullOrWhiteSpace(item))
            .Cast<string>()
            .ToList();

        var duplicateSkuCode = explicitSkuCodes
            .GroupBy(item => item, StringComparer.Ordinal)
            .FirstOrDefault(group => group.Count() > 1)?
            .Key;
        if (!string.IsNullOrWhiteSpace(duplicateSkuCode))
        {
            throw new BusinessException(ErrorCodes.BadRequest, $"SKU 编码重复: {duplicateSkuCode}");
        }

        if (explicitSkuCodes.Count > 0)
        {
            string? codeConflict;
            using (dbContext.DisableSoftDeleteFilter())
            {
                codeConflict = await dbContext.ProductSkus
                    .AsNoTracking()
                    .Where(item => item.ProductId != productId && explicitSkuCodes.Contains(item.SkuCode))
                    .Select(item => item.SkuCode)
                    .FirstOrDefaultAsync(cancellationToken);
            }

            if (!string.IsNullOrWhiteSpace(codeConflict))
            {
                throw new BusinessException(ErrorCodes.BadRequest, $"SKU 编码已存在: {codeConflict}");
            }
        }

        List<ProductSku> existingSkus;
        using (dbContext.DisableSoftDeleteFilter())
        {
            existingSkus = await dbContext.ProductSkus
                .Where(item => item.ProductId == productId)
                .OrderBy(item => item.Id)
                .ToListAsync(cancellationToken);
        }

        var existingBySkuCode = existingSkus
            .Where(item => !string.IsNullOrWhiteSpace(item.SkuCode))
            .GroupBy(item => item.SkuCode, StringComparer.Ordinal)
            .ToDictionary(group => group.Key, group => new Queue<ProductSku>(group), StringComparer.Ordinal);

        var existingByAttrKey = existingSkus
            .GroupBy(item => BuildSkuAttributeKey(ParseSkuAttributes(item.AttributesJson)), StringComparer.Ordinal)
            .ToDictionary(group => group.Key, group => new Queue<ProductSku>(group), StringComparer.Ordinal);

        var usedExistingSkuIds = new HashSet<long>();
        var plannedSkuCodes = new HashSet<string>(StringComparer.Ordinal);
        var currentProductSkuCodes = existingSkus
            .Where(item => !string.IsNullOrWhiteSpace(item.SkuCode))
            .Select(item => item.SkuCode)
            .ToHashSet(StringComparer.Ordinal);
        foreach (var explicitSkuCode in explicitSkuCodes)
        {
            currentProductSkuCodes.Add(explicitSkuCode);
        }
        var createdSkus = new List<ProductSku>();

        foreach (var sku in normalizedSkus)
        {
            var normalizedSkuCode = NormalizeSkuCode(sku.SkuCode);
            var attrKey = BuildSkuAttributeKey(sku.Attributes);

            ProductSku? matched = null;
            if (!string.IsNullOrWhiteSpace(normalizedSkuCode) &&
                existingBySkuCode.TryGetValue(normalizedSkuCode, out var byCodeQueue))
            {
                matched = PickUnmatchedSku(byCodeQueue, usedExistingSkuIds);
            }

            if (matched is null && existingByAttrKey.TryGetValue(attrKey, out var byAttrQueue))
            {
                matched = PickUnmatchedSku(byAttrQueue, usedExistingSkuIds);
            }

            if (matched is not null)
            {
                usedExistingSkuIds.Add(matched.Id);
                var targetSkuCode = !string.IsNullOrWhiteSpace(normalizedSkuCode)
                    ? normalizedSkuCode
                    : NormalizeSkuCode(matched.SkuCode);
                if (string.IsNullOrWhiteSpace(targetSkuCode))
                {
                    targetSkuCode = GenerateUniqueSkuCode(productId, currentProductSkuCodes);
                }

                if (!plannedSkuCodes.Add(targetSkuCode))
                {
                    throw new BusinessException(ErrorCodes.BadRequest, $"SKU 编码冲突: {targetSkuCode}");
                }

                currentProductSkuCodes.Add(targetSkuCode);
                matched.Price = sku.Price;
                matched.OriginalPrice = sku.OriginalPrice;
                matched.StockQuantity = Math.Max(0, sku.Stock);
                matched.AttributesJson = SerializeSkuAttributes(sku.Attributes);
                matched.SortOrder = sku.SortOrder;
                matched.IsEnabled = sku.IsEnabled;
                matched.DeletedAt = null;
                matched.DeletedBy = null;
                matched.SkuCode = targetSkuCode;

                continue;
            }

            var generatedCode = !string.IsNullOrWhiteSpace(normalizedSkuCode)
                ? normalizedSkuCode
                : GenerateUniqueSkuCode(productId, currentProductSkuCodes);
            if (!plannedSkuCodes.Add(generatedCode))
            {
                throw new BusinessException(ErrorCodes.BadRequest, $"SKU 编码冲突: {generatedCode}");
            }

            currentProductSkuCodes.Add(generatedCode);
            createdSkus.Add(new ProductSku
            {
                ProductId = productId,
                SkuCode = generatedCode,
                Price = sku.Price,
                OriginalPrice = sku.OriginalPrice,
                StockQuantity = Math.Max(0, sku.Stock),
                AttributesJson = SerializeSkuAttributes(sku.Attributes),
                SortOrder = sku.SortOrder,
                IsEnabled = sku.IsEnabled
            });
        }

        foreach (var existing in existingSkus)
        {
            if (usedExistingSkuIds.Contains(existing.Id))
            {
                continue;
            }

            if (!string.IsNullOrWhiteSpace(existing.SkuCode) && plannedSkuCodes.Contains(existing.SkuCode))
            {
                throw new BusinessException(ErrorCodes.BadRequest, $"SKU 编码已存在: {existing.SkuCode}");
            }

            existing.IsEnabled = false;
            existing.StockQuantity = 0;
        }

        if (createdSkus.Count > 0)
        {
            await dbContext.ProductSkus.AddRangeAsync(createdSkus, cancellationToken);
        }

        try
        {
            await dbContext.SaveChangesAsync(cancellationToken);
        }
        catch (DbUpdateException ex) when (IsSkuCodeUniqueViolation(ex))
        {
            throw new BusinessException(ErrorCodes.BadRequest, "SKU 编码冲突，请刷新后重试");
        }
    }

    private async Task ValidateSkuTemplateRefsAsync(
        long storeId,
        IReadOnlyList<ProductSkuUpsertInput> skus,
        IReadOnlyCollection<long> specTemplateIds,
        CancellationToken cancellationToken)
    {
        var allowedSpecTemplateIds = specTemplateIds.ToHashSet();
        var templateIdsInSkus = skus
            .SelectMany(item => item.Attributes)
            .Select(item => item.TemplateId)
            .Distinct()
            .ToList();

        var outOfSelectedTemplateId = templateIdsInSkus.FirstOrDefault(item => !allowedSpecTemplateIds.Contains(item));
        if (outOfSelectedTemplateId > 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, $"SKU 使用了未关联的规格模板: {outOfSelectedTemplateId}");
        }

        if (templateIdsInSkus.Count > 0)
        {
            var templateTypeLookup = await dbContext.ProductSpecTemplates
                .AsNoTracking()
                .Where(item => item.StoreId == storeId && templateIdsInSkus.Contains(item.Id))
                .ToDictionaryAsync(item => item.Id, item => item.TemplateType, cancellationToken);

            var missingTemplateId = templateIdsInSkus.FirstOrDefault(item => !templateTypeLookup.ContainsKey(item));
            if (missingTemplateId > 0)
            {
                throw new BusinessException(ErrorCodes.BadRequest, $"SKU 规格模板不存在: {missingTemplateId}");
            }

            var invalidTemplateId = templateTypeLookup
                .FirstOrDefault(item => item.Value == ProductSpecTemplateType.Addon)
                .Key;
            if (invalidTemplateId > 0)
            {
                throw new BusinessException(ErrorCodes.BadRequest, $"SKU 规格模板类型错误: {invalidTemplateId}");
            }
        }

        var optionIdsInSkus = skus
            .SelectMany(item => item.Attributes)
            .Select(item => item.OptionId)
            .Distinct()
            .ToList();

        var optionTemplateLookup = optionIdsInSkus.Count == 0
            ? new Dictionary<long, long>()
            : await dbContext.ProductSpecTemplateOptions
                .AsNoTracking()
                .Where(item => optionIdsInSkus.Contains(item.Id))
                .ToDictionaryAsync(item => item.Id, item => item.TemplateId, cancellationToken);

        var missingOptionId = optionIdsInSkus.FirstOrDefault(item => !optionTemplateLookup.ContainsKey(item));
        if (missingOptionId > 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, $"SKU 规格选项不存在: {missingOptionId}");
        }

        foreach (var sku in skus)
        {
            foreach (var attr in sku.Attributes)
            {
                if (optionTemplateLookup[attr.OptionId] != attr.TemplateId)
                {
                    throw new BusinessException(
                        ErrorCodes.BadRequest,
                        $"SKU 规格选项与模板不匹配: templateId={attr.TemplateId}, optionId={attr.OptionId}");
                }
            }
        }
    }

    private static ProductSku? PickUnmatchedSku(Queue<ProductSku> queue, HashSet<long> usedExistingSkuIds)
    {
        while (queue.Count > 0)
        {
            var current = queue.Peek();
            if (usedExistingSkuIds.Contains(current.Id))
            {
                queue.Dequeue();
                continue;
            }

            return current;
        }

        return null;
    }

    private static string BuildSkuAttributeKey(IReadOnlyList<ProductSkuUpsertAttributeInput> attributes)
    {
        if (attributes.Count == 0)
        {
            return "default";
        }

        return string.Join('|', attributes
            .OrderBy(item => item.TemplateId)
            .ThenBy(item => item.OptionId)
            .Select(item => $"{item.TemplateId}:{item.OptionId}"));
    }

    private static List<ProductSkuUpsertAttributeInput> ParseSkuAttributes(string? attributesJson)
    {
        if (string.IsNullOrWhiteSpace(attributesJson))
        {
            return [];
        }

        try
        {
            var parsed = JsonSerializer.Deserialize<List<SkuAttributePayload>>(attributesJson, StoreApiHelpers.JsonOptions) ?? [];
            return parsed
                .Where(item => item.TemplateId > 0 && item.OptionId > 0)
                .Select(item => new ProductSkuUpsertAttributeInput
                {
                    TemplateId = item.TemplateId,
                    OptionId = item.OptionId
                })
                .DistinctBy(item => $"{item.TemplateId}:{item.OptionId}")
                .ToList();
        }
        catch
        {
            return [];
        }
    }

    private static string SerializeSkuAttributes(IReadOnlyList<ProductSkuUpsertAttributeInput> attributes)
    {
        if (attributes.Count == 0)
        {
            return "[]";
        }

        var payload = attributes
            .OrderBy(item => item.TemplateId)
            .ThenBy(item => item.OptionId)
            .Select(item => new SkuAttributePayload(item.TemplateId, item.OptionId))
            .ToList();
        return JsonSerializer.Serialize(payload);
    }

    private static string NormalizeSkuCode(string? skuCode)
    {
        var normalized = (skuCode ?? string.Empty).Trim();
        return string.IsNullOrWhiteSpace(normalized) ? string.Empty : normalized;
    }

    private static string GenerateUniqueSkuCode(long productId, IReadOnlySet<string> currentProductSkuCodes)
    {
        for (var i = 0; i < 200; i++)
        {
            var random = RandomNumberGenerator.GetInt32(0, 60_466_176);
            var candidate = $"SKU{productId}{ToBase36((ulong)random).PadLeft(5, '0')}";
            if (!currentProductSkuCodes.Contains(candidate))
            {
                return candidate;
            }
        }

        throw new BusinessException(ErrorCodes.InternalServerError, "SKU 编码生成失败，请稍后重试");
    }

    private static string ToBase36(ulong value)
    {
        const string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        if (value == 0)
        {
            return "0";
        }

        Span<char> buffer = stackalloc char[16];
        var pos = buffer.Length;
        while (value > 0)
        {
            buffer[--pos] = chars[(int)(value % 36)];
            value /= 36;
        }

        return new string(buffer[pos..]);
    }

    private static bool IsSkuCodeUniqueViolation(DbUpdateException exception)
    {
        if (exception.InnerException is not PostgresException postgresException)
        {
            return false;
        }

        return postgresException.SqlState == PostgresErrorCodes.UniqueViolation &&
               string.Equals(
                   postgresException.ConstraintName,
                   "IX_product_skus_TenantId_SkuCode",
                   StringComparison.Ordinal);
    }

    private sealed record SkuAttributePayload(long TemplateId, long OptionId);
}

/// <summary>
/// SKU replace 输入模型。
/// </summary>
public sealed class ProductSkuUpsertInput
{
    /// <summary>
    /// SKU 编码（可选）。
    /// </summary>
    public string? SkuCode { get; set; }

    /// <summary>
    /// 售价。
    /// </summary>
    public decimal Price { get; set; }

    /// <summary>
    /// 划线价。
    /// </summary>
    public decimal? OriginalPrice { get; set; }

    /// <summary>
    /// 库存。
    /// </summary>
    public int Stock { get; set; }

    /// <summary>
    /// 是否启用。
    /// </summary>
    public bool IsEnabled { get; set; } = true;

    /// <summary>
    /// 排序值。
    /// </summary>
    public int SortOrder { get; set; }

    /// <summary>
    /// 规格属性组合。
    /// </summary>
    public List<ProductSkuUpsertAttributeInput> Attributes { get; set; } = [];
}

/// <summary>
/// SKU replace 属性输入模型。
/// </summary>
public sealed class ProductSkuUpsertAttributeInput
{
    /// <summary>
    /// 规格模板 ID。
    /// </summary>
    public long TemplateId { get; set; }

    /// <summary>
    /// 模板选项 ID。
    /// </summary>
    public long OptionId { get; set; }
}

/// <summary>
/// SKU 异步保存任务负载。
/// </summary>
public sealed class ProductSkuSaveJobPayload
{
    /// <summary>
    /// SKU 列表快照。
    /// </summary>
    public List<ProductSkuUpsertInput> Skus { get; set; } = [];

    /// <summary>
    /// 允许使用的规格模板 ID 列表。
    /// </summary>
    public List<long> SpecTemplateIds { get; set; } = [];
}
