using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Tiku.Application.Assets;
using Tiku.Application.Learning;
using Tiku.Application.PlatformAdmin;
using Tiku.Application.Security;
using Tiku.Domain.Common;
using Tiku.Domain.Content;
using Tiku.Domain.Operations;
using Tiku.Domain.QuestionBanks;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Persistence;

namespace Tiku.Infrastructure.PlatformAdmin;

internal abstract partial class PlatformQuestionBankServiceBase
{
    protected Task<IReadOnlyCollection<PlatformQuestionBankNodeItem>> GetNodesCoreAsync(
        PlatformAdminActor actor,
        Guid bankId,
        CancellationToken cancellationToken = default)
    {
        return ExecuteAsync<IReadOnlyCollection<PlatformQuestionBankNodeItem>>(actor, "查询公共题库内容结构",
            async (_, dbContext, tenantId, token) =>
            {
                var bank = await RequireBankWithEntryAsync(dbContext, tenantId, bankId, token);
                var nodes = await dbContext.ContentNodes.AsNoTracking()
                    .Where(item => item.TenantId == tenantId && item.EntryId == bank.ContentEntryId)
                    .OrderBy(item => item.Depth).ThenBy(item => item.SortOrder).ThenBy(item => item.Name)
                    .ToArrayAsync(token);
                var counts = await dbContext.Questions.AsNoTracking()
                    .Where(item =>
                        item.TenantId == tenantId && item.QuestionBankId == bankId && item.ContentNodeId.HasValue &&
                        item.Status != QuestionStatus.Archived)
                    .GroupBy(item => item.ContentNodeId!.Value)
                    .Select(group => new { NodeId = group.Key, Count = group.Count() })
                    .ToDictionaryAsync(item => item.NodeId, item => item.Count, token);
                return nodes.Select(item =>
                    ToNodeItem(item, bankId, counts.TryGetValue(item.Id, out var count) ? count : 0)).ToArray();
            }, cancellationToken);
    }

    protected Task<PlatformQuestionBankNodeItem> UpsertNodeCoreServiceAsync(
        PlatformAdminActor actor,
        UpsertPlatformQuestionBankNodeCommand command,
        CancellationToken cancellationToken = default)
    {
        return ExecuteAsync(actor, "保存公共题库内容节点", async (_, dbContext, tenantId, token) =>
        {
            var bank = await RequireBankWithEntryAsync(dbContext, tenantId, command.QuestionBankId, token);
            return await UpsertNodeCoreAsync(dbContext, actor, tenantId, bank, command, token);
        }, cancellationToken);
    }

    protected Task<IReadOnlyCollection<PlatformQuestionBankNodeItem>> BatchCreateNodesCoreAsync(
        PlatformAdminActor actor,
        BatchCreatePlatformQuestionBankNodesCommand command,
        CancellationToken cancellationToken = default)
    {
        return ExecuteAsync<IReadOnlyCollection<PlatformQuestionBankNodeItem>>(actor, "批量创建章节或试卷",
            async (_, dbContext, tenantId, token) =>
            {
                if (command.NodeType is not ContentNodeType.Chapter and not ContentNodeType.Paper)
                    throw Error("批量创建仅支持章节或试卷。", "node_batch_type_invalid");

                var bank = await RequireBankWithEntryAsync(dbContext, tenantId, command.QuestionBankId, token);
                var names = command.Names.Select(item => item.Trim()).Where(item => item.Length > 0)
                    .Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
                if (names.Length is 0 or > 100) throw Error("请提供 1 至 100 个不重复的名称。", "node_batch_names_invalid");

                var result = new List<PlatformQuestionBankNodeItem>();
                var order = 0;
                foreach (var name in names)
                    result.Add(await UpsertNodeCoreAsync(dbContext, actor, tenantId, bank,
                        new UpsertPlatformQuestionBankNodeCommand(
                            null, command.QuestionBankId, command.ParentId, null, name, command.NodeType, order++, true,
                            JsonDefaults.Object()), token));

                return result;
            }, cancellationToken);
    }

    protected Task<PlatformQuestionBankNodeItem> ArchiveNodeCoreAsync(
        PlatformAdminActor actor,
        Guid nodeId,
        CancellationToken cancellationToken = default)
    {
        return ExecuteAsync(actor, "归档公共题库内容节点", async (_, dbContext, tenantId, token) =>
        {
            var node = await dbContext.ContentNodes.SingleOrDefaultAsync(
                           item => item.TenantId == tenantId && item.Id == nodeId, token)
                       ?? throw Error("内容节点不存在。", "question_bank_node_not_found");
            if (await dbContext.ContentNodes.AnyAsync(
                    item => item.TenantId == tenantId && item.ParentId == nodeId && item.IsActive, token))
                throw Error("该节点仍有启用的下级节点，不能归档。", "question_bank_node_has_children");

            if (await dbContext.Questions.AnyAsync(
                    item => item.TenantId == tenantId && item.ContentNodeId == nodeId &&
                            item.Status != QuestionStatus.Archived, token))
                throw Error("该节点仍有未归档题目，不能归档。", "question_bank_node_has_questions");

            node.IsActive = false;
            node.UpdatedBy = actor.UserId;
            var bankId = await dbContext.QuestionBanks
                .Where(item => item.TenantId == tenantId && item.ContentEntryId == node.EntryId).Select(item => item.Id)
                .SingleAsync(token);
            AddAudit(dbContext, actor, "platform.question_bank.node_archived", node.Id, new { node.Name });
            await dbContext.SaveChangesAsync(token);
            return ToNodeItem(node, bankId, 0);
        }, cancellationToken);
    }

    private static async Task<PlatformQuestionBankNodeItem> UpsertNodeCoreAsync(IPlatformQuestionBankAdministrationPersistence dbContext,
        PlatformAdminActor actor, Guid tenantId, QuestionBank bank, UpsertPlatformQuestionBankNodeCommand command,
        CancellationToken token)
    {
        if (string.IsNullOrWhiteSpace(command.Name)) throw Error("节点名称不能为空。", "question_bank_node_name_required");
        ContentNode? parent = null;
        if (command.ParentId.HasValue)
            parent = await RequireNodeAsync(dbContext, tenantId, bank, command.ParentId.Value, token);
        var node = command.Id.HasValue
            ? await dbContext.ContentNodes.SingleOrDefaultAsync(
                item => item.TenantId == tenantId && item.Id == command.Id.Value && item.EntryId == bank.ContentEntryId,
                token)
            : null;
        if (command.Id.HasValue && node is null) throw Error("内容节点不存在。", "question_bank_node_not_found");
        node ??= new ContentNode
        { TenantId = tenantId, EntryId = bank.ContentEntryId!.Value, CreatedBy = actor.UserId };
        if (!command.Id.HasValue) dbContext.ContentNodes.Add(node);
        node.ParentId = parent?.Id;
        node.NodeKey = string.IsNullOrWhiteSpace(command.NodeKey) ? $"node-{node.Id:N}" : command.NodeKey.Trim();
        node.Name = command.Name.Trim();
        node.NodeType = command.NodeType;
        node.Depth = parent is null ? 0 : parent.Depth + 1;
        node.Path = parent is null ? $"n{node.Id:N}" : $"{parent.Path}.n{node.Id:N}";
        node.SortOrder = command.SortOrder;
        node.IsActive = true;
        node.IsSelectable = command.IsSelectable;
        node.IsLeaf = command.NodeType is ContentNodeType.Chapter or ContentNodeType.Paper;
        node.Metadata = ObjectOrDefault(command.Metadata);
        node.UpdatedBy = actor.UserId;
        AddAudit(dbContext, actor, "platform.question_bank.node_saved", node.Id,
            new { bankId = bank.Id, node.Name, node.NodeType });
        await dbContext.SaveChangesAsync(token);
        return ToNodeItem(node, bank.Id, 0);
    }

}
