using FluentResults;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.Logging;
using NexusReader.Application.Abstractions.Services;

namespace NexusReader.UI.Shared.Services;

public class SyncService : ISyncService, IAsyncDisposable
{
    private readonly HttpClient _httpClient;
    private readonly INativeStorageService _storageService;
    private readonly IPlatformService _platformService;
    private readonly ILogger<SyncService> _logger;
    private HubConnection? _hubConnection;
    private bool _isInitialized;
    private CancellationTokenSource? _debounceCts;

    public event Func<string, DateTime, Task>? OnProgressReceived;
    public event Func<string, double, Task>? OnIngestionProgressReceived;

    public SyncService(
        HttpClient httpClient,
        INativeStorageService storageService,
        IPlatformService platformService,
        ILogger<SyncService> logger)
    {
        _httpClient = httpClient;
        _storageService = storageService;
        _platformService = platformService;
        _logger = logger;
    }

    public async Task<Result> InitializeAsync()
    {
        if (_isInitialized) return Result.Ok();

        var tokenResult = await _storageService.GetSecureString("nexus_auth_token");
        if (tokenResult.IsFailed) return Result.Fail("Not authenticated");

        var baseUrl = _httpClient.BaseAddress?.ToString() ?? "http://localhost:5000/";
        var hubUrl = new Uri(new Uri(baseUrl), "synchub").ToString();

        _hubConnection = new HubConnectionBuilder()
            .WithUrl(hubUrl, options =>
            {
                options.AccessTokenProvider = () => Task.FromResult<string?>(tokenResult.Value);
            })
            .WithAutomaticReconnect()
            .Build();

        _hubConnection.On<string, DateTime>("ProgressUpdated", async (pageId, timestamp) =>
        {
            // Note: In the future we might want to receive ebookId and progress here too
            if (pageId == _lastSentPageId)
            {
                _logger.LogDebug("[Sync] Ignoring self progress update for page {PageId}.", pageId);
                return;
            }
            _lastSentPageId = pageId; // Prevent echoing back duplicate progress updates
            if (OnProgressReceived != null) await OnProgressReceived(pageId, timestamp);
        });

        _hubConnection.On<string, double>("IngestionProgress", async (message, progress) =>
        {
            if (OnIngestionProgressReceived != null) await OnIngestionProgressReceived(message, progress);
        });

        try
        {
            await _hubConnection.StartAsync();
            _isInitialized = true;
            return Result.Ok();
        }
        catch (Exception ex)
        {
            return Result.Fail(ex.Message);
        }
    }

    private string? _lastSentPageId;

    public async Task<Result> UpdateProgressAsync(string pageId, Guid ebookId, double progress, string? chapterTitle, int chapterIndex)
    {
        if (pageId == _lastSentPageId) return Result.Ok();
        
        _lastSentPageId = pageId;
        
        // Proper trailing-edge debounce
        _debounceCts?.Cancel();
        _debounceCts = new CancellationTokenSource();
        var token = _debounceCts.Token;

        _ = Task.Run(async () =>
        {
            try
            {
                await Task.Delay(2000, token);

                if (!_isInitialized) await InitializeAsync();

                if (_hubConnection?.State == HubConnectionState.Connected)
                {
                    await _hubConnection.SendAsync("UpdateProgress", pageId, ebookId, progress, chapterTitle, chapterIndex);
                }
            }
            catch (TaskCanceledException) { /* Ignored, user kept scrolling */ }
            catch (Exception ex)
            {
                _logger.LogError(ex, "[SyncService] Error sending reading progress for page {PageId}.", pageId);
            }
        }, token);

        return Result.Ok();
    }

    public async Task DisposeAsync()
    {
        _debounceCts?.Cancel();
        if (_hubConnection != null)
        {
            await _hubConnection.DisposeAsync();
        }
    }

    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        await DisposeAsync();
    }
}
