using adas_core.Application.Services.Interfaces;
using adas_core.Domain.Models.AppSettings;
using EasyNetQ;
using EasyNetQ.SystemMessages;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;

namespace adas_core.Infrastructure.Services;

/// <summary>
/// Represents a service that implements the <see cref="IPublisherService"/> contract, providing the concrete implementation of the publishing operations defined by the interface.
/// </summary>
public class PublisherService : IPublisherService
{
    private readonly ILogger<PublisherService> _logger;
    private readonly HashSet<string> _queues = new();
    private readonly IBus? _bus;

    public PublisherService(
        IOptions<RabbitMqSettings> rabbitMqSettings,
        ILogger<PublisherService> logger)
    {
        _logger = logger;

        try
        {
            var connection = rabbitMqSettings.Value.ConnectionString;

            if (string.IsNullOrWhiteSpace(connection))
            {
                _logger.LogError("Missing RabbitMQ connection string");
                return;
            }

            var services = new ServiceCollection();
            services.AddEasyNetQ(connection);

            var provider = services.BuildServiceProvider();
            _bus = provider.GetRequiredService<IBus>();

            _logger.LogInformation("PublisherService initialized");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error initializing PublisherService");
        }
    }

    public Task<bool> CreateQueue(string queueName)
    {
        if (string.IsNullOrWhiteSpace(queueName))
            return Task.FromResult(false);

        if (_queues.Contains(queueName))
        {
            _logger.LogDebug("Queue {queueName} already registered", queueName);
            return Task.FromResult(true);
        }

        _queues.Add(queueName);

        _logger.LogInformation("Queue registered: {queueName}", queueName);

        // EasyNetQ crea colas automáticamente
        return Task.FromResult(true);
    }

    /// <summary>
    /// Sends a message to the specified queue using the configured bus. If the bus is not initialized or an error occurs, the operation is logged and <c>false</c> is returned.
    /// </summary>
    /// <param name="msg">The message payload to send to the queue.</param>
    /// <param name="queueName">The name of the target queue to which the message will be sent.</param>
    /// <returns><c>true</c> if the message was sent successfully; otherwise, <c>false</c>.</returns>
    public async Task<bool> SendMessage(string msg, string queueName)
        {
            try
            {
                if (_bus == null)
                {
                    _logger.LogError("Bus is null");
                    return false;
                }
    
                await CreateQueue(queueName);
    
                _logger.LogDebug("Sending message to {queueName}", queueName);
    
                await _bus.SendReceive.SendAsync(queueName, msg);
    
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error sending message to {queueName}", queueName);
                return false;
            }
        }

    /// <summary>
    /// Serializes the specified object to JSON and asynchronously sends it to the named queue.
    /// </summary>
    /// <param name="obj">The object to serialize and send to the queue.</param>
    /// <param name="queueName">The name of the destination queue.</param>
    /// <returns>A task that resolves to <c>true</c> if the message was sent successfully; otherwise, <c>false</c>.</returns>
    public async Task<bool> SendMessage(object obj, string queueName)
        {
            var json = JsonConvert.SerializeObject(obj);
            return await SendMessage(json, queueName);
        }

    /// <summary>
    /// Sends an error message to the specified queue. Validates that the bus instance is available and that the supplied object is a <c>Message&lt;Error&gt;</c>; returns <c>false</c> when either validation fails or when the send operation throws an exception.
    /// </summary>
    /// <param name="obj">The message object expected to be a <c>Message&lt;Error&gt;</c>. If it is not, the method returns <c>false</c> without sending anything.</param>
    /// <param name="queueName">The name of the queue to which the error message body will be sent. The queue is created if it does not already exist.</param>
    /// <returns>A <see cref="Task{Boolean}"/> that resolves to <c>true</c> when the error message is successfully sent, and <c>false</c> when the bus is null, the object is not a <c>Message&lt;Error&gt;</c>, or an exception is raised while sending.</returns>
    public async Task<bool> SendMessageError(object obj, string queueName)
        {
            try
            {
                if (_bus == null)
                {
                    _logger.LogError("Bus is null - cannot send error message");
                    return false;
                }
    
                if (obj is not Message<Error> errorMessage)
                    return false;
    
                await CreateQueue(queueName);
    
                _logger.LogDebug("Sending error message to {queueName}", queueName);
    
                // enviamos solo el Error (no Message<Error>)
                await _bus.SendReceive.SendAsync(queueName, errorMessage.Body);
    
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error sending error message");
                return false;
            }
        }
}
