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<PlatformQuestionBankItem>> GetBanksCoreAsync(
        PlatformAdminActor actor,
        PlatformQuestionBankFilter filter,
        CancellationToken cancellationToken = default)
    {
        return ExecuteAsync<IReadOnlyCollection<PlatformQuestionBankItem>>(actor, "查询平台公共题库",
            async (_, dbContext, tenantId, token) =>
            {
                var query = dbContext.QuestionBanks.AsNoTracking().Where(item => item.TenantId == tenantId);
                if (!string.IsNullOrWhiteSpace(filter.Keyword))
                {
                    var keyword = filter.Keyword.Trim();
                    query = query.Where(item => item.Name.Contains(keyword));
                }

                if (!string.IsNullOrWhiteSpace(filter.Status) &&
                    !filter.Status.Equals("all", StringComparison.OrdinalIgnoreCase))
                {
                    var status = ParseBankStatus(filter.Status);
                    query = query.Where(item => item.Status == status);
                }

                var banks = await query.OrderBy(item => item.Name).ThenBy(item => item.CreatedAt).ToArrayAsync(token);
                var entryIds = banks.Where(item => item.ContentEntryId.HasValue)
                    .Select(item => item.ContentEntryId!.Value).ToArray();
                var nodeCounts = await dbContext.ContentNodes.AsNoTracking()
                    .Where(item => item.TenantId == tenantId && entryIds.Contains(item.EntryId) && item.IsActive)
                    .GroupBy(item => item.EntryId)
                    .Select(group => new { EntryId = group.Key, Count = group.Count() })
                    .ToDictionaryAsync(item => item.EntryId, item => item.Count, token);
                var questionCounts = await dbContext.Questions.AsNoTracking()
                    .Where(item =>
                        item.TenantId == tenantId && item.QuestionBankId.HasValue &&
                        item.Status != QuestionStatus.Archived)
                    .GroupBy(item => item.QuestionBankId!.Value)
                    .Select(group => new { BankId = group.Key, Count = group.Count() })
                    .ToDictionaryAsync(item => item.BankId, item => item.Count, token);
                return banks.Select(item => ToBankItem(
                    item,
                    item.ContentEntryId.HasValue && nodeCounts.TryGetValue(item.ContentEntryId.Value, out var nodes)
                        ? nodes
                        : 0,
                    questionCounts.TryGetValue(item.Id, out var questions) ? questions : 0)).ToArray();
            }, cancellationToken);
    }

    protected Task<PlatformQuestionBankItem> UpsertBankCoreAsync(
        PlatformAdminActor actor,
        UpsertPlatformQuestionBankCommand command,
        CancellationToken cancellationToken = default)
    {
        return ExecuteAsync(actor, "保存平台公共题库", async (_, dbContext, tenantId, token) =>
        {
            if (string.IsNullOrWhiteSpace(command.Name)) throw Error("题库名称不能为空。", "question_bank_name_required");

            var bank = command.Id.HasValue
                ? await dbContext.QuestionBanks.SingleOrDefaultAsync(
                    item => item.TenantId == tenantId && item.Id == command.Id.Value, token)
                : null;
            if (command.Id.HasValue && bank is null) throw Error("公共题库不存在。", "question_bank_not_found");

            ContentEntry? trackedEntry = null;
            if (bank is null)
            {
                var entry = new ContentEntry
                {
                    TenantId = tenantId,
                    EntryKey = $"public-question-bank-{Guid.NewGuid():N}",
                    Name = command.Name.Trim(),
                    EntryType = ContentEntryType.QuestionPractice,
                    Visibility = ContentVisibility.Public,
                    IsActive = true,
                    CreatedBy = actor.UserId,
                    UpdatedBy = actor.UserId
                };
                trackedEntry = entry;
                bank = new QuestionBank
                {
                    TenantId = tenantId,
                    ContentEntryId = entry.Id,
                    Status = QuestionBankStatus.Active
                };
                dbContext.ContentEntries.Add(entry);
                dbContext.QuestionBanks.Add(bank);
            }
            else if (!bank.ContentEntryId.HasValue)
            {
                var entry = new ContentEntry
                {
                    TenantId = tenantId,
                    EntryKey = $"public-question-bank-{bank.Id:N}",
                    Name = command.Name.Trim(),
                    EntryType = ContentEntryType.QuestionPractice,
                    Visibility = ContentVisibility.Public,
                    IsActive = true,
                    CreatedBy = actor.UserId,
                    UpdatedBy = actor.UserId
                };
                trackedEntry = entry;
                dbContext.ContentEntries.Add(entry);
                bank.ContentEntryId = entry.Id;
            }

            bank.Name = command.Name.Trim();
            bank.Metadata = ObjectOrDefault(command.Metadata);
            if (bank.ContentEntryId.HasValue)
            {
                var entry = trackedEntry ??
                            await dbContext.ContentEntries.SingleAsync(
                                item => item.TenantId == tenantId && item.Id == bank.ContentEntryId.Value, token);
                entry.Name = bank.Name;
                entry.UpdatedBy = actor.UserId;
            }

            AddAudit(dbContext, actor, "platform.question_bank.saved", bank.Id, new { bank.Name });
            await dbContext.SaveChangesAsync(token);
            return ToBankItem(bank, 0, 0);
        }, cancellationToken);
    }

    protected Task<PlatformQuestionBankItem> ArchiveBankCoreAsync(
        PlatformAdminActor actor,
        Guid bankId,
        CancellationToken cancellationToken = default)
    {
        return ExecuteAsync(actor, "归档平台公共题库", async (_, dbContext, tenantId, token) =>
        {
            var bank = await RequireBankAsync(dbContext, tenantId, bankId, token);
            if (await dbContext.Questions.AnyAsync(
                    item => item.TenantId == tenantId && item.QuestionBankId == bankId &&
                            item.Status != QuestionStatus.Archived, token))
                throw Error("题库中仍有未归档题目，不能归档题库。", "question_bank_not_empty");

            if (bank.ContentEntryId.HasValue && await dbContext.ContentNodes.AnyAsync(
                    item => item.TenantId == tenantId && item.EntryId == bank.ContentEntryId && item.IsActive, token))
                throw Error("题库中仍有启用的内容层级，不能归档题库。", "question_bank_nodes_not_archived");

            bank.Status = QuestionBankStatus.Archived;
            AddAudit(dbContext, actor, "platform.question_bank.archived", bank.Id, new { bank.Name });
            await dbContext.SaveChangesAsync(token);
            return ToBankItem(bank, 0, 0);
        }, cancellationToken);
    }

}
