using MediatR;
using FluentResults;
using Microsoft.EntityFrameworkCore;
using NexusReader.Application.DTOs.User;
using NexusReader.Data.Persistence;

namespace NexusReader.Application.Queries.User;

public class GetUserProfileQueryHandler : IRequestHandler<GetUserProfileQuery, Result<UserProfileDto>>
{
    private readonly IDbContextFactory<AppDbContext> _dbContextFactory;

    public GetUserProfileQueryHandler(IDbContextFactory<AppDbContext> dbContextFactory)
    {
        _dbContextFactory = dbContextFactory;
    }

    public async Task<Result<UserProfileDto>> Handle(GetUserProfileQuery request, CancellationToken cancellationToken)
    {
        using var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
        
        var userRaw = await dbContext.Users
            .Where(u => u.Id == request.UserId)
            .Select(u => new
            {
                Email = u.Email ?? string.Empty,
                UserId = u.Id,
                AITokensUsed = u.AITokensUsed,
                TenantIdString = u.TenantId,
                Plan = u.SubscriptionPlan != null ? new SubscriptionPlanDto
                {
                    Id = u.SubscriptionPlan.Id,
                    Name = u.SubscriptionPlan.PlanName,
                    AITokenLimit = u.SubscriptionPlan.AITokenLimit,
                    MonthlyPrice = u.SubscriptionPlan.MonthlyPrice
                } : new SubscriptionPlanDto(),
                QuizResults = u.QuizResults.Select(q => new
                {
                    q.Score,
                    q.TotalQuestions,
                    q.Id,
                    q.Topic,
                    q.Percentage,
                    q.CompletedDate
                }).ToList(),
                DisplayName = u.DisplayName,
                BooksReadCount = u.Ebooks.Count(),
                LastReadBook = u.Ebooks.OrderByDescending(e => e.LastReadDate).Select(e => new LastReadBookDto
                {
                    Id = e.Id,
                    Title = e.Title,
                    Author = new AuthorDto 
                    { 
                        Id = e.Author.Id, 
                        Name = e.Author.Name 
                    },
                    CoverUrl = e.CoverUrl,
                    Progress = e.Progress,
                    LastChapter = e.LastChapter ?? "Rozpoczynanie...",
                    LastChapterIndex = e.LastChapterIndex,
                    Description = e.Description,
                    IsReadyForReading = e.IsReadyForReading
                }).FirstOrDefault(),
                Roles = dbContext.UserRoles
                    .Where(ur => ur.UserId == u.Id)
                    .Join(dbContext.Roles, ur => ur.RoleId, r => r.Id, (ur, r) => r.Name!)
                    .ToArray()
            })
            .FirstOrDefaultAsync(cancellationToken);

        if (userRaw == null)
        {
            return Result.Fail("Profile not found.");
        }

        var tenantId = userRaw.TenantIdString;
        var mappedConcepts = await dbContext.KnowledgeUnits
            .Where(k => k.TenantId == tenantId || k.TenantId == "global" || string.IsNullOrEmpty(k.TenantId))
            .OrderByDescending(k => k.CreatedAt)
            .Take(6)
            .Select(k => new MappedConceptDto
            {
                Id = k.Id,
                Type = k.Type.ToString(),
                Content = k.Content
            })
            .ToListAsync(cancellationToken);

        var conceptsMappedCount = await dbContext.KnowledgeUnits
            .CountAsync(k => k.TenantId == tenantId || k.TenantId == "global" || string.IsNullOrEmpty(k.TenantId), cancellationToken);

        int averageQuizScore = 0;
        var validQuizzes = userRaw.QuizResults.Where(q => q.TotalQuestions > 0).ToList();
        if (validQuizzes.Count > 0)
        {
            averageQuizScore = (int)(validQuizzes.Average(q => (double)q.Score / q.TotalQuestions) * 100);
        }

        var profile = new UserProfileDto
        {
            Email = userRaw.Email,
            UserId = userRaw.UserId,
            AITokensUsed = userRaw.AITokensUsed,
            TenantId = userRaw.TenantIdString != null && userRaw.TenantIdString.Length == 36 ? new Guid(userRaw.TenantIdString) : Guid.Empty,
            Plan = userRaw.Plan,
            AverageQuizScore = averageQuizScore,
            DisplayName = userRaw.DisplayName,
            BooksReadCount = userRaw.BooksReadCount,
            ConceptsMappedCount = conceptsMappedCount,
            LastReadBook = userRaw.LastReadBook,
            RecentQuizzes = userRaw.QuizResults.OrderByDescending(q => q.CompletedDate).Take(5).Select(q => new QuizResultDto
            {
                Id = q.Id,
                Topic = q.Topic,
                Score = q.Score,
                TotalQuestions = q.TotalQuestions,
                Percentage = q.Percentage,
                CompletedDate = q.CompletedDate
            }).ToList(),
            MappedConcepts = mappedConcepts,
            Roles = userRaw.Roles
        };

        return Result.Ok(profile);
    }
}
