using System.Text.Json;
using TakeoutSaaS.Application.Dictionary.Abstractions;
using TakeoutSaaS.Application.Dictionary.Models;
using TakeoutSaaS.Domain.Dictionary.Entities;
using TakeoutSaaS.Domain.Dictionary.Enums;
using TakeoutSaaS.Domain.Dictionary.Repositories;
using TakeoutSaaS.Domain.Dictionary.ValueObjects;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;

namespace TakeoutSaaS.Application.Dictionary.Services;

/// <summary>
/// 租户字典覆盖配置服务。
/// </summary>
public sealed class DictionaryOverrideService(
    IDictionaryGroupRepository groupRepository,
    IDictionaryItemRepository itemRepository,
    ITenantDictionaryOverrideRepository overrideRepository,
    IDictionaryHybridCache cache)
{
    private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);

    /// <summary>
    /// 获取租户覆盖配置列表。
    /// </summary>
    public async Task<IReadOnlyList<OverrideConfigDto>> GetOverridesAsync(long tenantId, CancellationToken cancellationToken = default)
    {
        if (tenantId == 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "租户上下文缺失");
        }

        var configs = await overrideRepository.ListAsync(tenantId, cancellationToken);
        if (configs.Count == 0)
        {
            return Array.Empty<OverrideConfigDto>();
        }

        var groupIds = configs.Select(config => config.SystemDictionaryGroupId).Distinct().ToArray();
        var groups = await groupRepository.GetByIdsAsync(groupIds, cancellationToken);
        var codeMap = groups
            .Where(group => group.Scope == DictionaryScope.System)
            .ToDictionary(group => group.Id, group => group.Code);

        var result = new List<OverrideConfigDto>(configs.Count);
        foreach (var config in configs)
        {
            if (codeMap.TryGetValue(config.SystemDictionaryGroupId, out var code))
            {
                result.Add(MapOverrideDto(config, code));
            }
        }

        return result;
    }

    /// <summary>
    /// 获取租户指定分组的覆盖配置。
    /// </summary>
    public async Task<OverrideConfigDto?> GetOverrideAsync(long tenantId, string systemGroupCode, CancellationToken cancellationToken = default)
    {
        if (tenantId == 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "租户上下文缺失");
        }

        var systemGroup = await RequireSystemGroupAsync(systemGroupCode, cancellationToken);
        var config = await overrideRepository.GetAsync(tenantId, systemGroup.Id, cancellationToken);
        return config == null ? null : MapOverrideDto(config, systemGroup.Code);
    }

    /// <summary>
    /// 启用覆盖配置。
    /// </summary>
    public async Task<OverrideConfigDto> EnableOverrideAsync(long tenantId, string systemGroupCode, CancellationToken cancellationToken = default)
    {
        if (tenantId == 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "租户上下文缺失");
        }

        var systemGroup = await RequireSystemGroupAsync(systemGroupCode, cancellationToken);
        if (!systemGroup.AllowOverride)
        {
            throw new BusinessException(ErrorCodes.Forbidden, "该系统字典不允许租户覆盖");
        }

        var config = await overrideRepository.GetAsync(tenantId, systemGroup.Id, cancellationToken);
        if (config == null)
        {
            config = new TenantDictionaryOverride
            {
                TenantId = tenantId,
                SystemDictionaryGroupId = systemGroup.Id,
                OverrideEnabled = true,
                HiddenSystemItemIds = Array.Empty<long>(),
                CustomSortOrder = "{}"
            };
            await overrideRepository.AddAsync(config, cancellationToken);
        }
        else
        {
            config.OverrideEnabled = true;
            await overrideRepository.UpdateAsync(config, cancellationToken);
        }

        await overrideRepository.SaveChangesAsync(cancellationToken);
        await cache.InvalidateAsync(
            DictionaryCacheKeys.BuildDictionaryKey(tenantId, systemGroup.Code),
            CacheInvalidationOperation.Update,
            cancellationToken);

        return MapOverrideDto(config, systemGroup.Code);
    }

    /// <summary>
    /// 关闭覆盖配置。
    /// </summary>
    public async Task<bool> DisableOverrideAsync(long tenantId, string systemGroupCode, CancellationToken cancellationToken = default)
    {
        if (tenantId == 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "租户上下文缺失");
        }

        var systemGroup = await RequireSystemGroupAsync(systemGroupCode, cancellationToken);
        var config = await overrideRepository.GetAsync(tenantId, systemGroup.Id, cancellationToken);
        if (config == null)
        {
            return false;
        }

        config.OverrideEnabled = false;
        await overrideRepository.UpdateAsync(config, cancellationToken);
        await overrideRepository.SaveChangesAsync(cancellationToken);
        await cache.InvalidateAsync(
            DictionaryCacheKeys.BuildDictionaryKey(tenantId, systemGroup.Code),
            CacheInvalidationOperation.Update,
            cancellationToken);

        return true;
    }

    /// <summary>
    /// 更新隐藏系统字典项。
    /// </summary>
    public async Task<OverrideConfigDto> UpdateHiddenItemsAsync(
        long tenantId,
        string systemGroupCode,
        long[] hiddenIds,
        CancellationToken cancellationToken = default)
    {
        if (tenantId == 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "租户上下文缺失");
        }

        var systemGroup = await RequireSystemGroupAsync(systemGroupCode, cancellationToken);
        if (!systemGroup.AllowOverride)
        {
            throw new BusinessException(ErrorCodes.Forbidden, "该系统字典不允许租户覆盖");
        }

        var systemItems = await itemRepository.GetByGroupIdAsync(0, systemGroup.Id, cancellationToken);
        var validIds = systemItems.Select(item => item.Id).ToHashSet();
        var normalized = hiddenIds?.Distinct().ToArray() ?? Array.Empty<long>();

        if (normalized.Any(id => !validIds.Contains(id)))
        {
            throw new BusinessException(ErrorCodes.ValidationFailed, "隐藏项包含无效的系统字典项");
        }

        var config = await overrideRepository.GetAsync(tenantId, systemGroup.Id, cancellationToken);
        if (config == null)
        {
            config = new TenantDictionaryOverride
            {
                TenantId = tenantId,
                SystemDictionaryGroupId = systemGroup.Id,
                OverrideEnabled = true,
                HiddenSystemItemIds = normalized,
                CustomSortOrder = "{}"
            };
            await overrideRepository.AddAsync(config, cancellationToken);
        }
        else
        {
            config.HiddenSystemItemIds = normalized;
            await overrideRepository.UpdateAsync(config, cancellationToken);
        }

        await overrideRepository.SaveChangesAsync(cancellationToken);
        await cache.InvalidateAsync(
            DictionaryCacheKeys.BuildDictionaryKey(tenantId, systemGroup.Code),
            CacheInvalidationOperation.Update,
            cancellationToken);

        return MapOverrideDto(config, systemGroup.Code);
    }

    /// <summary>
    /// 更新自定义排序配置。
    /// </summary>
    public async Task<OverrideConfigDto> UpdateCustomSortOrderAsync(
        long tenantId,
        string systemGroupCode,
        Dictionary<long, int> sortOrderMap,
        CancellationToken cancellationToken = default)
    {
        if (tenantId == 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "租户上下文缺失");
        }

        var systemGroup = await RequireSystemGroupAsync(systemGroupCode, cancellationToken);
        if (!systemGroup.AllowOverride)
        {
            throw new BusinessException(ErrorCodes.Forbidden, "该系统字典不允许租户覆盖");
        }

        var validIds = await CollectValidItemIdsAsync(tenantId, systemGroup, cancellationToken);
        if (sortOrderMap.Keys.Any(id => !validIds.Contains(id)))
        {
            throw new BusinessException(ErrorCodes.ValidationFailed, "排序配置包含无效的字典项");
        }

        var config = await overrideRepository.GetAsync(tenantId, systemGroup.Id, cancellationToken);
        if (config == null)
        {
            config = new TenantDictionaryOverride
            {
                TenantId = tenantId,
                SystemDictionaryGroupId = systemGroup.Id,
                OverrideEnabled = true,
                HiddenSystemItemIds = Array.Empty<long>(),
                CustomSortOrder = SerializeSortOrder(sortOrderMap)
            };
            await overrideRepository.AddAsync(config, cancellationToken);
        }
        else
        {
            config.CustomSortOrder = SerializeSortOrder(sortOrderMap);
            await overrideRepository.UpdateAsync(config, cancellationToken);
        }

        await overrideRepository.SaveChangesAsync(cancellationToken);
        await cache.InvalidateAsync(
            DictionaryCacheKeys.BuildDictionaryKey(tenantId, systemGroup.Code),
            CacheInvalidationOperation.Update,
            cancellationToken);

        return MapOverrideDto(config, systemGroup.Code);
    }

    private async Task<HashSet<long>> CollectValidItemIdsAsync(long tenantId, DictionaryGroup systemGroup, CancellationToken cancellationToken)
    {
        var validIds = (await itemRepository.GetByGroupIdAsync(0, systemGroup.Id, cancellationToken))
            .Select(item => item.Id)
            .ToHashSet();

        var tenantGroup = await groupRepository.GetByCodeAsync(tenantId, systemGroup.Code, cancellationToken);
        if (tenantGroup != null)
        {
            foreach (var item in await itemRepository.GetByGroupIdAsync(tenantId, tenantGroup.Id, cancellationToken))
            {
                validIds.Add(item.Id);
            }
        }

        return validIds;
    }

    private async Task<DictionaryGroup> RequireSystemGroupAsync(string code, CancellationToken cancellationToken)
    {
        var normalized = new DictionaryCode(code);
        var group = await groupRepository.GetByCodeAsync(0, normalized, cancellationToken);
        if (group == null)
        {
            throw new BusinessException(ErrorCodes.NotFound, "系统字典分组不存在");
        }

        if (group.Scope != DictionaryScope.System)
        {
            throw new BusinessException(ErrorCodes.ValidationFailed, "仅支持系统字典覆盖配置");
        }

        return group;
    }

    private static OverrideConfigDto MapOverrideDto(TenantDictionaryOverride config, DictionaryCode systemCode)
    {
        return new OverrideConfigDto
        {
            TenantId = config.TenantId,
            SystemDictionaryGroupCode = systemCode.Value,
            OverrideEnabled = config.OverrideEnabled,
            HiddenSystemItemIds = config.HiddenSystemItemIds,
            CustomSortOrder = ParseSortOrder(config.CustomSortOrder)
        };
    }

    private static Dictionary<long, int> ParseSortOrder(string? json)
    {
        if (string.IsNullOrWhiteSpace(json))
        {
            return new Dictionary<long, int>();
        }

        try
        {
            return JsonSerializer.Deserialize<Dictionary<long, int>>(json, JsonOptions) ?? new Dictionary<long, int>();
        }
        catch (JsonException)
        {
            return new Dictionary<long, int>();
        }
    }

    private static string SerializeSortOrder(Dictionary<long, int> map)
        => JsonSerializer.Serialize(map ?? new Dictionary<long, int>(), JsonOptions);
}
