using Microsoft.EntityFrameworkCore;
using Tiku.Application.Content;
using Tiku.Application.Security;
using Tiku.Domain.QuestionBanks;

namespace Tiku.Infrastructure.Content;

public sealed partial class DirectContentService
{
    public async Task<ContentManagementResult<QuestionManagementItem>> CreateQuestionAsync(
        DirectContentActor actor,
        QuestionWriteCommand command,
        CancellationToken cancellationToken = default)
    {
        ValidateQuestionForPublication(command);
        await AssertQuestionReferencesAsync(actor.TenantId, command, cancellationToken);
        await using var transaction = dbContext.Database.CurrentTransaction is null
            ? await dbContext.Database.BeginTransactionAsync(cancellationToken)
            : null;

        var question = new Question
        {
            Id = command.QuestionId ?? Guid.NewGuid(),
            TenantId = actor.TenantId
        };
        ApplyQuestion(question, command);
        if (question.Status != QuestionStatus.Archived)
            await featureAccessService.ConsumeQuotaIfConfiguredAsync(
                actor.TenantId,
                SaasQuotaMetricCatalog.PrivateQuestionCount,
                cancellationToken: cancellationToken);
        dbContext.Questions.Add(question);
        await dbContext.SaveChangesAsync(cancellationToken);

        var version = BuildQuestionVersion(actor, question.Id, 1, command);
        dbContext.QuestionVersions.Add(version);
        question.CurrentVersionId = version.Id;
        await SyncPrimaryCollectionItemAsync(actor, question, cancellationToken);

        await dbContext.SaveChangesAsync(cancellationToken);
        if (transaction is not null) await transaction.CommitAsync(cancellationToken);
        return new ContentManagementResult<QuestionManagementItem>(ToQuestionItem(question, version));
    }

    public async Task<ContentManagementResult<QuestionManagementItem>> UpdateQuestionAsync(
        DirectContentActor actor,
        QuestionWriteCommand command,
        CancellationToken cancellationToken = default)
    {
        if (!command.QuestionId.HasValue)
            throw new ContentManagementException("questionId is required.", "question_id_required");

        ValidateQuestionForPublication(command);

        var question = await dbContext.Questions.SingleOrDefaultAsync(
            item => item.TenantId == actor.TenantId && item.Id == command.QuestionId.Value,
            cancellationToken);
        if (question is null) throw new ContentManagementException("Question was not found.", "question_not_found");

        await AssertQuestionReferencesAsync(actor.TenantId, command, cancellationToken);
        await using var transaction = dbContext.Database.CurrentTransaction is null
            ? await dbContext.Database.BeginTransactionAsync(cancellationToken)
            : null;
        var wasCounted = question.Status != QuestionStatus.Archived;
        ApplyQuestion(question, command);
        var isCounted = question.Status != QuestionStatus.Archived;
        if (!wasCounted && isCounted)
            await featureAccessService.ConsumeQuotaIfConfiguredAsync(
                actor.TenantId,
                SaasQuotaMetricCatalog.PrivateQuestionCount,
                cancellationToken: cancellationToken);
        QuestionVersion? version;
        if (command.CreateVersion || !question.CurrentVersionId.HasValue)
        {
            var nextVersionNo = await dbContext.QuestionVersions
                .Where(item => item.TenantId == actor.TenantId && item.QuestionId == question.Id)
                .Select(item => (int?)item.VersionNo)
                .MaxAsync(cancellationToken) ?? 0;
            version = BuildQuestionVersion(actor, question.Id, nextVersionNo + 1, command);
            dbContext.QuestionVersions.Add(version);
            question.CurrentVersionId = version.Id;
        }
        else
        {
            version = await dbContext.QuestionVersions.SingleOrDefaultAsync(
                item =>
                    item.TenantId == actor.TenantId &&
                    item.QuestionId == question.Id &&
                    item.Id == question.CurrentVersionId.Value,
                cancellationToken);
            if (version is null)
            {
                version = BuildQuestionVersion(actor, question.Id, 1, command);
                dbContext.QuestionVersions.Add(version);
                question.CurrentVersionId = version.Id;
            }
            else
            {
                ApplyQuestionVersion(version, command);
            }
        }

        await SyncPrimaryCollectionItemAsync(actor, question, cancellationToken);

        await dbContext.SaveChangesAsync(cancellationToken);
        if (wasCounted && !isCounted)
            await featureAccessService.ReleaseQuotaAsync(
                actor.TenantId,
                SaasQuotaMetricCatalog.PrivateQuestionCount,
                1,
                cancellationToken);
        if (transaction is not null) await transaction.CommitAsync(cancellationToken);
        return new ContentManagementResult<QuestionManagementItem>(ToQuestionItem(question, version));
    }
}