using Microsoft.ApplicationInsights;
using Microsoft.Extensions.Hosting;
using Npgsql;
using PlanTempus.Core.Database.ConnectionFactory;
using PlanTempus.Core.Telemetry;

namespace PlanTempus.Components.Outbox;

public class OutboxListener : BackgroundService
{
	private readonly IDbConnectionFactory _connectionFactory;
	private readonly ICommandHandler _commandHandler;
	private readonly TelemetryClient _telemetryClient;
	private readonly IMessageChannel<string> _notificationChannel;

	public OutboxListener(
		IDbConnectionFactory connectionFactory,
		ICommandHandler commandHandler,
		TelemetryClient telemetryClient,
		IMessageChannel<string> notificationChannel)
	{
		_connectionFactory = connectionFactory;
		_commandHandler = commandHandler;
		_telemetryClient = telemetryClient;
		_notificationChannel = notificationChannel;
	}

	protected override async Task ExecuteAsync(CancellationToken stoppingToken)
	{
		Console.WriteLine("OutboxListener starting - listening for outbox_messages");
		_telemetryClient.TrackTrace("OutboxListener starting - listening for outbox_messages");

		while (!stoppingToken.IsCancellationRequested)
		{
			try
			{
				await ListenForNotificationsAsync(stoppingToken);
			}
			catch (Exception ex) when (ex is not OperationCanceledException)
			{
				Console.WriteLine($"OutboxListener error: {ex.Message}");
				_telemetryClient.TrackException(ex);
				await Task.Delay(5000, stoppingToken);
			}
		}
	}

	private async Task ListenForNotificationsAsync(CancellationToken stoppingToken)
	{
		await using var conn = (NpgsqlConnection)_connectionFactory.Create();
		await conn.OpenAsync(stoppingToken);

		conn.Notification += (_, e) =>
		{
			Console.WriteLine($"Notification event received: {e.Payload}");
			_notificationChannel.Writer.TryWrite(e.Payload);
		};

		await using (var cmd = new NpgsqlCommand("LISTEN outbox_messages;", conn))
		{
			await cmd.ExecuteNonQueryAsync(stoppingToken);
		}

		Console.WriteLine("OutboxListener now listening on outbox_messages channel");
		_telemetryClient.TrackTrace("OutboxListener now listening on outbox_messages channel");

		// Process any pending messages on startup
		Console.WriteLine("Processing pending messages on startup...");
		await _commandHandler.Handle(new ProcessOutboxCommand());

		while (!stoppingToken.IsCancellationRequested)
		{
			await conn.WaitAsync(stoppingToken);

			while (_notificationChannel.Reader.TryRead(out var payload))
			{
				Console.WriteLine($"Outbox notification received from channel: {payload}");
				_telemetryClient.TrackTrace($"Outbox notification received: {payload}");

				try
				{
					await _commandHandler.Handle(new ProcessOutboxCommand());
				}
				catch (Exception ex)
				{
					Console.WriteLine($"Error processing outbox: {ex.Message}");
					_telemetryClient.TrackException(ex);
				}
			}
		}
	}

	public override async Task StopAsync(CancellationToken cancellationToken)
	{
		_telemetryClient.TrackTrace("OutboxListener stopping");
		await base.StopAsync(cancellationToken);
	}
}
