using NexusReader.Application.Abstractions.Services;
using FluentResults;
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, IAsyncDisposable
{
    private readonly IKnowledgeService _knowledgeService;
    private readonly IKnowledgeGraphService _graphService;
    private readonly IQuizStateService _quizService;
    private readonly IPlatformService _platformService;
    private readonly IReaderInteractionService _interactionService;
    private readonly ILogger<KnowledgeCoordinator> _logger;

    private CancellationTokenSource? _graphCts;
    private CancellationTokenSource? _quizCts;
    
    public string CurrentFullPageContent { get; private set; } = string.Empty;
    
    public bool IsLoadingSelectionSummary { get; private set; }
    public string? SelectionSummary { get; private set; }
    public string? SelectedTextContext { get; private set; }
    
    /// <summary>
    /// Raised when the knowledge graph has been updated with new data.
    /// Subscribers must return a Task to enable proper async handling.
    /// </summary>
    public event Func<GraphDataDto, Task>? OnGraphUpdated;

    /// <summary>
    /// Raised when the selection summary state has changed (loading started, finished, or cleared).
    /// </summary>
    public event Func<Task>? OnSelectionSummaryStateChanged;

    public KnowledgeCoordinator(
        IKnowledgeService knowledgeService,
        IKnowledgeGraphService graphService,
        IQuizStateService quizService,
        IPlatformService platformService,
        IReaderInteractionService interactionService,
        ILogger<KnowledgeCoordinator> logger)
    {
        _knowledgeService = knowledgeService;
        _graphService = graphService;
        _quizService = quizService;
        _platformService = platformService;
        _interactionService = interactionService;
        _logger = logger;

        _interactionService.OnNodeSelected += HandleNodeSelected;
    }

    private async Task HandleNodeSelected(string nodeId)
    {
        string? targetBlockId = nodeId;
        
        var graph = _graphService.CurrentGraphData;
        if (graph != null)
        {
            var selectedNode = graph.Nodes.FirstOrDefault(n => n.Id == nodeId);
            if (selectedNode != null && selectedNode.Group == "concept")
            {
                // Look for connected block nodes (group: "current") in the links
                var connectedLinks = graph.Links.Where(l => l.Source == nodeId || l.Target == nodeId).ToList();
                foreach (var link in connectedLinks)
                {
                    var otherId = link.Source == nodeId ? link.Target : link.Source;
                    var otherNode = graph.Nodes.FirstOrDefault(n => n.Id == otherId);
                    if (otherNode != null && otherNode.Group == "current")
                    {
                        targetBlockId = otherId;
                        break;
                    }
                }
            }
        }

        if (!string.IsNullOrEmpty(targetBlockId))
        {
            await _interactionService.RequestScrollToBlock(targetBlockId);
            await _interactionService.RequestHighlightBlock(targetBlockId);
        }
    }

    private void CancelAndDisposeCts(ref CancellationTokenSource? cts)
    {
        var localCts = cts;
        cts = null;
        if (localCts != null)
        {
            try
            {
                localCts.Cancel();
            }
            catch (ObjectDisposedException) { }
            finally
            {
                localCts.Dispose();
            }
        }
    }

    public async Task ProcessFullPageAsync(string fullContent, string tenantId = "global", Guid? ebookId = null)
    {
        if (string.IsNullOrWhiteSpace(fullContent))
        {
            CancelAndDisposeCts(ref _graphCts);
            await _graphService.Clear();
            await _graphService.SetLoading(false);
            CurrentFullPageContent = string.Empty;
            return;
        }

        CancelAndDisposeCts(ref _graphCts);
        _graphCts = new CancellationTokenSource();
        var token = _graphCts.Token;

        CurrentFullPageContent = fullContent;
        LogGeneratingGraph(tenantId);
        
        await _graphService.Clear();
        await _graphService.SetLoading(true);
        
        try
        {
            var result = await _knowledgeService.GetGraphDataAsync(fullContent, tenantId, ebookId, token);
            token.ThrowIfCancellationRequested();

            if (result.IsSuccess)
            {
                var packet = result.Value;
                if (packet.Graph != null)
                {
                    await _graphService.UpdateGraph(packet.Graph);
                    if (OnGraphUpdated != null)
                        await OnGraphUpdated.Invoke(packet.Graph);
                    await _platformService.VibrateSuccessAsync();
                    return;
                }
            }
            
            await _graphService.SetLoading(false);
        }
        catch (OperationCanceledException)
        {
            _logger.LogInformation("[KnowledgeCoordinator] Graph generation task was canceled.");
        }
        catch (Exception ex)
        {
            if (!token.IsCancellationRequested)
            {
                await _graphService.SetLoading(false);
                LogGraphError(ex, tenantId);
            }
        }
    }

    public async Task OnBlockReachedAsync(string blockId, string content)
    {
        // Only update active node for "TU JESTEŚ" logic, do NOT trigger highlight here
        await _graphService.SetActiveNode(blockId);
    }

    public async Task<Result<KnowledgePacket>> RequestSummaryAndQuizAsync(string content, string tenantId = "global")
    {
        CancelAndDisposeCts(ref _quizCts);
        _quizCts = new CancellationTokenSource();
        var token = _quizCts.Token;

        await _quizService.SetHydrating(true);
        LogRequestingSummary(tenantId);
        try
        {
            var result = await _knowledgeService.GetSummaryAndQuizAsync(content, tenantId, cancellationToken: token);
            token.ThrowIfCancellationRequested();

            if (result.IsSuccess)
            {
                var packet = result.Value;
                var quizQuestions = packet.Quizzes
                    .Select(q => new QuizQuestionDto(q.Question, q.Options, q.CorrectIndex))
                    .ToList();
                
                await _quizService.SetQuiz(null, new QuizDto(quizQuestions));
                await _platformService.VibrateSuccessAsync();
                return Result.Ok(packet);
            }

            LogSummaryWarning(tenantId);
            return Result.Fail(result.Errors);
        }
        catch (OperationCanceledException)
        {
            _logger.LogInformation("[KnowledgeCoordinator] Quiz and summary generation task was canceled.");
            return Result.Fail("Task canceled");
        }
        catch (Exception ex)
        {
            if (!token.IsCancellationRequested)
            {
                LogSummaryError(ex, tenantId);
                return Result.Fail(new Error("Error requesting summary and quiz").CausedBy(ex));
            }
            return Result.Fail("Task canceled");
        }
        finally
        {
            await _quizService.SetHydrating(false);
        }
    }

    public async Task StartSelectionSummaryAsync(string text, string tenantId = "global")
    {
        if (string.IsNullOrWhiteSpace(text)) return;
        
        IsLoadingSelectionSummary = true;
        SelectionSummary = null;
        SelectedTextContext = text;
        if (OnSelectionSummaryStateChanged != null)
        {
            await OnSelectionSummaryStateChanged.Invoke();
        }

        try
        {
            var result = await RequestSummaryAndQuizAsync(text, tenantId);
            if (result.IsSuccess)
            {
                SelectionSummary = result.Value.Summary;
            }
            else
            {
                _logger.LogWarning("[KnowledgeCoordinator] Selection summary request failed: {Errors}", string.Join(", ", result.Errors.Select(e => e.Message)));
            }
        }
        finally
        {
            IsLoadingSelectionSummary = false;
            if (OnSelectionSummaryStateChanged != null)
            {
                await OnSelectionSummaryStateChanged.Invoke();
            }
        }
    }

    public async Task ClearSelectionSummaryAsync()
    {
        SelectionSummary = null;
        SelectedTextContext = null;
        IsLoadingSelectionSummary = false;
        if (OnSelectionSummaryStateChanged != null)
        {
            await OnSelectionSummaryStateChanged.Invoke();
        }
    }

    public async Task ClearAsync()
    {
        CancelAndDisposeCts(ref _graphCts);
        CancelAndDisposeCts(ref _quizCts);

        CurrentFullPageContent = string.Empty;
        await _graphService.Clear();
        await _quizService.SetQuiz(null, null);
        await ClearSelectionSummaryAsync();
    }

    public void Dispose()
    {
        _interactionService.OnNodeSelected -= HandleNodeSelected;

        CancelAndDisposeCts(ref _graphCts);
        CancelAndDisposeCts(ref _quizCts);
    }

    public async ValueTask DisposeAsync()
    {
        _interactionService.OnNodeSelected -= HandleNodeSelected;

        CancelAndDisposeCts(ref _graphCts);
        CancelAndDisposeCts(ref _quizCts);

        try
        {
            await _graphService.Clear();
            await _quizService.SetQuiz(null, null);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Error clearing services during KnowledgeCoordinator disposal.");
        }
    }

    [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);
}
