// <copyright file="UdpPortListener.cs" company="IGORock Software">
// Copyright (c) IGORock Software. All rights reserved.
// </copyright>
// <author>Igor Kozlov</author>

using System.Net;
using System.Text;
using System.Text.Json;

using Forwarder.Data;
using Forwarder.Repositories;

using Microsoft.Extensions.Logging;

using Serilog;

namespace Forwarder.Modules.Ademco;

/// <summary>
/// Слушает UDP порт.
/// </summary>
internal sealed class UdpPortListener(
	ILogger<UdpPortListener> logger,
	Ademco685MessageParser parser,
	IMessageRepository messageRepository)
{
	/// <summary>
	/// Начинает слушать UDP порт.
	/// </summary>
	/// <param name="port">Порт.</param>
	/// <param name="cancellationToken">Токен отмены.</param>
	/// <returns>Задача.</returns>
	public async Task Start(ushort port, CancellationToken cancellationToken)
	{
		logger.LogInformation("Start listener for port {Port}", port);

		while (!cancellationToken.IsCancellationRequested)
		{
			try
			{
				using var udpTransfer = new UdpReader(port);

				await Listen(udpTransfer, cancellationToken);
			}
			catch (Exception ex)
			{
				Log.Error(ex, "Error in Listener.Start()");
			}

			await Task.Delay(1000, cancellationToken);
		}
	}

	private async Task Listen(UdpReader udpTransfer, CancellationToken cancellationToken)
	{
		while (!cancellationToken.IsCancellationRequested)
		{
			var result = await udpTransfer.Read(cancellationToken);
			if (result.IsFailure)
			{
				Log.Error("Read UDP data is failure: {Error}", result.Error);
				await Task.Delay(1000, cancellationToken);
				continue;
			}

			await OnDataReadAsync(result.Value, udpTransfer, cancellationToken);
		}
	}

	private async Task OnDataReadAsync(
		DataReadResult input,
		UdpReader udpReader,
		CancellationToken cancellationToken)
	{
		var data = input.Data;

		var sourceString = Encoding.ASCII.GetString(data, 0, data.Length);
		Log.Information(
			"Ademco [{Port}]: {Address} -> {Message}",
			udpReader.Port,
			input.EndPoint,
			sourceString.Replace("\n", "[LF]").Replace("\r", "[CR]")
		);

		udpReader.Write([0x06], input.EndPoint);
		Log.Information("Reply the 0x06 answer");

		var parsed = parser.Parse(sourceString);

		// если сообщение тестовое - возвращаем ответ
		if (parsed.IsFailure)
		{
			Log.Information("Ademco: Message fail to parse.");
			return;
		}

		if (parsed.Value.IsTest)
		{
			Log.Information("Ademco: Test message received.");
			return;
		}

		// если сообщение нормальное - пересылаем в тсп
		var message = parsed.Value.Message
			?? throw new InvalidOperationException("Parsed message is null");

		Log.Information("Message: {Message}", JsonSerializer.Serialize(message));
		var addMessageResult = await messageRepository.AddAsync(message, cancellationToken);
		if (addMessageResult.IsFail)
		{
			Log.Error("Error by message add: {Error}", addMessageResult.Error);
		}

		Log.Information("The message: {Message} saved in database", sourceString);
	}
}
