using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Tiku.Application.Learning;
using Tiku.Application.QuestionBanks;
using Tiku.Domain.Content;
using Tiku.Domain.Learning;

namespace Tiku.Infrastructure.Learning;

public sealed partial class LearningActivityService
{
    public async Task<LearningList<FavoriteQuestionItem>> GetFavoriteQuestionsAsync(
        LearningActor actor,
        LearningLimitFilter filter,
        CancellationToken cancellationToken = default)
    {
        var items = await dbContext.FavoriteQuestions
            .AsNoTracking()
            .Where(item =>
                item.TenantId == actor.TenantId &&
                item.UserId == actor.UserId)
            .OrderByDescending(item => item.CreatedAt)
            .Take(ResolveLimit(filter.Limit))
            .Select(item => new FavoriteQuestionItem(
                item.QuestionReferenceId,
                new QuestionLocator(
                    item.QuestionOwnerTenantId == item.TenantId ? QuestionSource.Tenant : QuestionSource.Platform,
                    item.QuestionId),
                item.CreatedAt))
            .ToArrayAsync(cancellationToken);

        return new LearningList<FavoriteQuestionItem>(items);
    }

    public async Task<LearningActionResult> ToggleFavoriteQuestionAsync(
        LearningActor actor,
        QuestionActionCommand command,
        CancellationToken cancellationToken = default)
    {
        var reference = await questionReferenceService.ResolveAsync(
            actor.TenantId,
            actor.UserId,
            command.Locator,
            cancellationToken);

        var favorite = command.Favorite ?? true;
        var item = await dbContext.FavoriteQuestions.FindAsync(
            [actor.TenantId, actor.UserId, reference.Id],
            cancellationToken);

        if (favorite)
        {
            if (item is null)
                dbContext.FavoriteQuestions.Add(new FavoriteQuestion
                {
                    TenantId = actor.TenantId,
                    UserId = actor.UserId,
                    QuestionReferenceId = reference.Id,
                    QuestionOwnerTenantId = reference.QuestionOwnerTenantId,
                    QuestionId = reference.QuestionId,
                    Source = reference.Source.ToString().ToLowerInvariant(),
                    CreatedAt = DateTimeOffset.UtcNow
                });
        }
        else if (item is not null)
        {
            dbContext.FavoriteQuestions.Remove(item);
        }

        await dbContext.SaveChangesAsync(cancellationToken);
        return new LearningActionResult(true, favorite);
    }

    public async Task<LearningList<WrongQuestionItem>> GetWrongQuestionsAsync(
        LearningActor actor,
        LearningLimitFilter filter,
        CancellationToken cancellationToken = default)
    {
        var query = dbContext.WrongQuestions
            .AsNoTracking()
            .Where(item =>
                item.TenantId == actor.TenantId &&
                item.UserId == actor.UserId);

        if (!string.Equals(filter.Status, "all", StringComparison.OrdinalIgnoreCase))
            query = query.Where(item => item.ResolvedAt == null);

        var items = await query
            .OrderByDescending(item => item.LastWrongAt)
            .Take(ResolveLimit(filter.Limit))
            .Select(item => new WrongQuestionItem(
                item.QuestionReferenceId,
                new QuestionLocator(
                    item.QuestionOwnerTenantId == item.TenantId ? QuestionSource.Tenant : QuestionSource.Platform,
                    item.QuestionId),
                item.WrongCount,
                item.LastWrongAt,
                item.ResolvedAt))
            .ToArrayAsync(cancellationToken);

        return new LearningList<WrongQuestionItem>(items);
    }

    public async Task<LearningActionResult> ResolveWrongQuestionAsync(
        LearningActor actor,
        QuestionActionCommand command,
        CancellationToken cancellationToken = default)
    {
        var reference = await questionReferenceService.ResolveAsync(
            actor.TenantId,
            actor.UserId,
            command.Locator,
            cancellationToken);
        var item = await dbContext.WrongQuestions.FindAsync(
            [actor.TenantId, actor.UserId, reference.Id],
            cancellationToken);

        if (item is null)
            throw new LearningResourceNotFoundException("wrong_question_not_found", "Wrong question was not found.");

        item.ResolvedAt = DateTimeOffset.UtcNow;
        await dbContext.SaveChangesAsync(cancellationToken);
        return new LearningActionResult(true);
    }

    public async Task<WrongQuestionReviewPlan> GetWrongQuestionReviewPlanAsync(
        LearningActor actor,
        LearningLimitFilter filter,
        CancellationToken cancellationToken = default)
    {
        var items = await dbContext.WrongQuestions.AsNoTracking()
            .Where(item =>
                item.TenantId == actor.TenantId &&
                item.UserId == actor.UserId &&
                item.ResolvedAt == null)
            .OrderByDescending(item => item.WrongCount)
            .ThenBy(item => item.LastWrongAt)
            .Take(ResolveLimit(filter.Limit))
            .Select(item => new WrongQuestionReviewPlanItem(
                item.QuestionReferenceId,
                new QuestionLocator(
                    item.QuestionOwnerTenantId == item.TenantId ? QuestionSource.Tenant : QuestionSource.Platform,
                    item.QuestionId),
                item.WrongCount,
                item.LastWrongAt))
            .ToArrayAsync(cancellationToken);
        return new WrongQuestionReviewPlan(
            items,
            JsonSerializer.SerializeToElement(new
            {
                mode = "wrong_review",
                questionCount = items.Length,
                recommendedEndpoint = "/api/student/learning/practice-sessions"
            }));
    }
}