using NexusReader.Application.Abstractions.Services; using NexusReader.Application.Queries.Graph; using NexusReader.Application.Queries.Quiz; using NexusReader.UI.Shared.Services; using NexusReader.Application.DTOs.AI; using Microsoft.Extensions.Logging; namespace NexusReader.UI.Shared.Services; public sealed partial class KnowledgeCoordinator : IDisposable { private readonly IKnowledgeService _knowledgeService; private readonly IKnowledgeGraphService _graphService; private readonly IQuizStateService _quizService; private readonly IPlatformService _platformService; private readonly IReaderInteractionService _interactionService; private readonly ILogger _logger; public event Action? OnGraphUpdated; public KnowledgeCoordinator( IKnowledgeService knowledgeService, IKnowledgeGraphService graphService, IQuizStateService quizService, IPlatformService platformService, IReaderInteractionService interactionService, ILogger logger) { _knowledgeService = knowledgeService; _graphService = graphService; _quizService = quizService; _platformService = platformService; _interactionService = interactionService; _logger = logger; _interactionService.OnNodeSelected += HandleNodeSelected; } private void HandleNodeSelected(string nodeId) { _interactionService.RequestScrollToBlock(nodeId); _interactionService.RequestHighlightBlock(nodeId); } public async Task ProcessFullPageAsync(string fullContent, string tenantId = "global") { if (string.IsNullOrWhiteSpace(fullContent)) return; LogGeneratingGraph(tenantId); _graphService.Clear(); _graphService.SetLoading(true); try { var result = await _knowledgeService.GetGraphDataAsync(fullContent, tenantId); if (result.IsSuccess) { var packet = result.Value; if (packet.Graph != null) { _graphService.UpdateGraph(packet.Graph); OnGraphUpdated?.Invoke(packet.Graph); await _platformService.VibrateSuccessAsync(); } } } catch (Exception ex) { LogGraphError(ex, tenantId); } } public void OnBlockReached(string blockId, string content) { // Only update active node for "TU JESTEĊš" logic, do NOT trigger highlight here _graphService.SetActiveNode(blockId); } public async Task RequestSummaryAndQuizAsync(string content, string tenantId = "global") { _quizService.SetHydrating(true); LogRequestingSummary(tenantId); try { var result = await _knowledgeService.GetSummaryAndQuizAsync(content, tenantId); if (result.IsSuccess) { var packet = result.Value; var quizQuestions = packet.Quizzes .Select(q => new QuizQuestionDto(q.Question, q.Options, q.CorrectIndex)) .ToList(); _quizService.SetQuiz(null, new QuizDto(quizQuestions)); await _platformService.VibrateSuccessAsync(); return packet; } LogSummaryWarning(tenantId); } catch (Exception ex) { LogSummaryError(ex, tenantId); } finally { _quizService.SetHydrating(false); } return null; } public void Clear() { _graphService.Clear(); _quizService.SetQuiz(null, null); } public void Dispose() { _interactionService.OnNodeSelected -= HandleNodeSelected; } [LoggerMessage(Level = LogLevel.Information, Message = "[KnowledgeCoordinator] Generating full page graph for tenant: {TenantId}")] private partial void LogGeneratingGraph(string tenantId); [LoggerMessage(Level = LogLevel.Error, Message = "[KnowledgeCoordinator] Error generating graph for tenant: {TenantId}")] private partial void LogGraphError(Exception ex, string tenantId); [LoggerMessage(Level = LogLevel.Information, Message = "[KnowledgeCoordinator] Requesting summary and quiz for tenant: {TenantId}")] private partial void LogRequestingSummary(string tenantId); [LoggerMessage(Level = LogLevel.Warning, Message = "[KnowledgeCoordinator] Failed to get summary and quiz for tenant: {TenantId}")] private partial void LogSummaryWarning(string tenantId); [LoggerMessage(Level = LogLevel.Error, Message = "[KnowledgeCoordinator] Error requesting summary and quiz for tenant: {TenantId}")] private partial void LogSummaryError(Exception ex, string tenantId); }