using FluentResults;
using System.Linq;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NexusReader.Application.Abstractions.Messaging;
using NexusReader.Application.Abstractions.Persistence;
using NexusReader.Application.Abstractions.Services;
using NexusReader.Domain.Entities;

namespace NexusReader.Application.Commands.Library;

public class IngestEbookCommandHandler : IRequestHandler<IngestEbookCommand, Result<Guid>>
{
    private readonly IEbookRepository _ebookRepository;
    private readonly IBookStorageService _storageService;
    private readonly IServiceScopeFactory _scopeFactory;

    public IngestEbookCommandHandler(
        IEbookRepository ebookRepository,
        IBookStorageService storageService,
        IServiceScopeFactory scopeFactory)
    {
        _ebookRepository = ebookRepository;
        _storageService = storageService;
        _scopeFactory = scopeFactory;
    }

    public async Task<Result<Guid>> Handle(IngestEbookCommand request, CancellationToken cancellationToken)
    {
        string epubPath;
        string? coverUrl;

        try
        {
            // 1. Save Files
            epubPath = await _storageService.SaveEbookAsync(request.EpubData, $"{request.Title}.epub");
            coverUrl = request.CoverImage != null && request.CoverImage.Length > 0
                ? await _storageService.SaveCoverAsync(request.CoverImage, $"{request.Title}_cover.jpg")
                : null;
        }
        catch (IOException ex)
        {
            return Result.Fail(new Error($"Storage I/O failure: {ex.Message}").CausedBy(ex));
        }
        catch (Exception ex)
        {
            return Result.Fail(new Error($"Storage failure: {ex.Message}").CausedBy(ex));
        }

        try
        {
            // 2. Resolve Author (case-insensitive via repository)
            var authorName = string.IsNullOrWhiteSpace(request.AuthorName)
                ? "Unknown Author"
                : request.AuthorName.Trim();

            var author = await _ebookRepository.FindAuthorByNameAsync(authorName, cancellationToken);
            if (author == null)
            {
                author = new Author { Name = authorName };
                _ebookRepository.AddAuthor(author);
            }

            // 3. Create Ebook
            var ebook = new Ebook
            {
                Title = request.Title,
                Author = author,
                FilePath = epubPath,
                CoverUrl = coverUrl,
                Description = request.Description,
                UserId = request.UserId,
                TenantId = request.TenantId,
                AddedDate = DateTime.UtcNow
            };

            _ebookRepository.AddEbook(ebook);
            await _ebookRepository.SaveChangesAsync(cancellationToken);

            // 4. Trigger asynchronous background processing and vector indexing
            _ = Task.Run(async () =>
            {
                using var scope = _scopeFactory.CreateScope();
                var logger = scope.ServiceProvider.GetRequiredService<ILogger<IngestEbookCommandHandler>>();
                var broadcaster = scope.ServiceProvider.GetRequiredService<ISyncBroadcaster>();
                try
                {
                    var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
                    var result = await mediator.Send(new ProcessEbookCommand(ebook.Id, request.UserId, request.TenantId));
                    if (result.IsFailed)
                    {
                        var errorMsg = string.Join("; ", result.Errors.Select(e => e.Message));
                        logger.LogError("[IngestEbook] Background ebook processing failed for Ebook {EbookId}: {Error}", ebook.Id, errorMsg);
                        await broadcaster.BroadcastIngestionProgressAsync(
                            request.UserId,
                            $"Błąd indeksowania: {errorMsg}",
                            1.0);
                    }
                }
                catch (Exception ex)
                {
                    logger.LogError(ex, "[IngestEbook] Exception during background ebook processing for Ebook {EbookId}", ebook.Id);
                    try
                    {
                        await broadcaster.BroadcastIngestionProgressAsync(
                            request.UserId,
                            $"Błąd krytyczny podczas przetwarzania e-booka: {ex.Message}",
                            1.0);
                    }
                    catch
                    {
                        // Ignore broadcast failures to prevent crashes
                    }
                }
            });

            return Result.Ok(ebook.Id);
        }
        catch (Exception ex)
        {
            return Result.Fail(new Error($"Database error during ingestion: {ex.Message}").CausedBy(ex));
        }
    }
}
