using TakeoutSaaS.Application.Dictionary.Abstractions;
using TakeoutSaaS.Application.Dictionary.Contracts;
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;
using TakeoutSaaS.Shared.Abstractions.Results;

namespace TakeoutSaaS.Application.Dictionary.Services;

/// <summary>
/// 字典查询服务。
/// </summary>
public sealed class DictionaryQueryService(
    IDictionaryGroupRepository groupRepository,
    IDictionaryItemRepository itemRepository,
    IDictionaryHybridCache cache)
{
    private static readonly TimeSpan CacheTtl = TimeSpan.FromMinutes(30);

    /// <summary>
    /// 获取字典分组分页数据。
    /// </summary>
    public async Task<PagedResult<DictionaryGroupDto>> GetGroupsAsync(
        DictionaryGroupQuery query,
        CancellationToken cancellationToken = default)
    {
        // 1. 解析查询租户与作用域
        var tenantId = query.TenantId ?? 0;
        if (query.Scope == DictionaryScope.Business && tenantId <= 0)
        {
            throw new BusinessException(ErrorCodes.ValidationFailed, "Scope=Business 时必须指定 TenantId");
        }
        
        // 2. (空行后) 确定作用域与目标租户
        var scope = query.Scope ?? (tenantId == 0 ? DictionaryScope.System : DictionaryScope.Business);
        if (scope == DictionaryScope.System)
        {
            tenantId = 0;
        }
        
        // 3. (空行后) 构建缓存键并加载分页数据
        var sortDescending = string.Equals(query.SortOrder, "desc", StringComparison.OrdinalIgnoreCase);
        var targetTenant = scope == DictionaryScope.System ? 0 : tenantId;

        var cacheKey = DictionaryCacheKeys.BuildGroupKey(
            targetTenant,
            scope,
            query.Page,
            query.PageSize,
            query.Keyword,
            query.IsEnabled,
            query.SortBy,
            sortDescending);

        var cached = await cache.GetOrCreateAsync<DictionaryGroupPage>(
            cacheKey,
            CacheTtl,
            async token =>
            {
                var groups = await groupRepository.GetPagedAsync(
                    targetTenant,
                    scope,
                    query.Keyword,
                    query.IsEnabled,
                    query.Page,
                    query.PageSize,
                    query.SortBy,
                    sortDescending,
                    token);

                var total = await groupRepository.CountAsync(
                    targetTenant,
                    scope,
                    query.Keyword,
                    query.IsEnabled,
                    token);

                var items = new List<DictionaryGroupDto>(groups.Count);
                foreach (var group in groups)
                {
                    IReadOnlyList<DictionaryItemDto>? groupItems = null;
                    if (query.IncludeItems)
                    {
                        var groupItemEntities = await itemRepository.GetByGroupIdAsync(group.TenantId, group.Id, token);
                        groupItems = groupItemEntities
                            .Where(item => item.IsEnabled)
                            .OrderBy(item => item.SortOrder)
                            .Select(DictionaryMapper.ToItemDto)
                            .ToList();
                    }

                    items.Add(DictionaryMapper.ToGroupDto(group, groupItems));
                }

                return new DictionaryGroupPage
                {
                    Items = items,
                    Page = query.Page,
                    PageSize = query.PageSize,
                    TotalCount = total
                };
            },
            cancellationToken);

        var page = cached ?? new DictionaryGroupPage
        {
            Items = Array.Empty<DictionaryGroupDto>(),
            Page = query.Page,
            PageSize = query.PageSize,
            TotalCount = 0
        };

        return new PagedResult<DictionaryGroupDto>(page.Items, page.Page, page.PageSize, page.TotalCount);
    }

    /// <summary>
    /// 获取字典分组详情。
    /// </summary>
    public async Task<DictionaryGroupDto?> GetGroupByIdAsync(long groupId, CancellationToken cancellationToken = default)
    {
        var group = await groupRepository.GetByIdAsync(groupId, cancellationToken);
        if (group == null)
        {
            return null;
        }

        return DictionaryMapper.ToGroupDto(group);
    }

    /// <summary>
    /// 获取分组下字典项列表。
    /// </summary>
    public async Task<IReadOnlyList<DictionaryItemDto>> GetItemsByGroupIdAsync(long groupId, CancellationToken cancellationToken = default)
    {
        var cacheKey = DictionaryCacheKeys.BuildItemKey(groupId);
        var cached = await cache.GetOrCreateAsync<IReadOnlyList<DictionaryItemDto>>(
            cacheKey,
            CacheTtl,
            async token =>
            {
                var group = await groupRepository.GetByIdAsync(groupId, token);
                if (group == null)
                {
                    throw new BusinessException(ErrorCodes.NotFound, "字典分组不存在");
                }

                var items = await itemRepository.GetByGroupIdAsync(group.TenantId, groupId, token);
                return items
                    .Where(item => item.IsEnabled)
                    .OrderBy(item => item.SortOrder)
                    .Select(DictionaryMapper.ToItemDto)
                    .ToList();
            },
            cancellationToken);

        return cached ?? Array.Empty<DictionaryItemDto>();
    }

    /// <summary>
    /// 获取合并后的字典项列表。
    /// </summary>
    public async Task<IReadOnlyList<DictionaryItemDto>> GetMergedDictionaryAsync(string code, CancellationToken cancellationToken = default)
    {
        if (!DictionaryCode.IsValid(code))
        {
            throw new BusinessException(ErrorCodes.ValidationFailed, "字典编码格式不正确");
        }

        // 1. 管理端默认读取系统字典（TenantId=0）
        var tenantId = 0;
        var normalized = new DictionaryCode(code);
        var cacheKey = DictionaryCacheKeys.BuildDictionaryKey(tenantId, normalized);

        var cached = await cache.GetOrCreateAsync<IReadOnlyList<DictionaryItemDto>>(
            cacheKey,
            CacheTtl,
            async token =>
            {
                var systemGroup = await groupRepository.GetByCodeAsync(0, normalized, token);
                if (systemGroup == null || !systemGroup.IsEnabled)
                {
                    return Array.Empty<DictionaryItemDto>();
                }

                var systemItems = await itemRepository.GetByGroupIdAsync(0, systemGroup.Id, token);
                return systemItems
                    .Where(item => item.IsEnabled)
                    .OrderBy(item => item.SortOrder)
                    .Select(DictionaryMapper.ToItemDto)
                    .ToList();
            },
            cancellationToken);

        return cached ?? Array.Empty<DictionaryItemDto>();
    }

    /// <summary>
    /// 批量获取字典项。
    /// </summary>
    public async Task<IReadOnlyDictionary<string, IReadOnlyList<DictionaryItemDto>>> BatchGetDictionariesAsync(
        IEnumerable<string> codes,
        CancellationToken cancellationToken = default)
    {
        var normalizedCodes = codes
            .Where(DictionaryCode.IsValid)
            .Select(code => new DictionaryCode(code).Value)
            .Distinct(StringComparer.OrdinalIgnoreCase)
            .ToArray();

        var result = new Dictionary<string, IReadOnlyList<DictionaryItemDto>>(StringComparer.OrdinalIgnoreCase);
        if (normalizedCodes.Length == 0)
        {
            return result;
        }

        var tasks = normalizedCodes.Select(async code =>
        {
            var items = await GetMergedDictionaryAsync(code, cancellationToken);
            return (code, items);
        });

        foreach (var pair in await Task.WhenAll(tasks))
        {
            result[pair.code] = pair.items;
        }

        return result;
    }

    private sealed class DictionaryGroupPage
    {
        public IReadOnlyList<DictionaryGroupDto> Items { get; init; } = Array.Empty<DictionaryGroupDto>();
        public int Page { get; init; }
        public int PageSize { get; init; }
        public int TotalCount { get; init; }
    }
}
