using System.Net;
using System.Text;
using adas_core.Application.Services;
using adas_core.Application.Services.Interfaces;
using adas_core.Domain.Models;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.Recording;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
using Moq.Protected;
using Newtonsoft.Json;
using Error = EasyNetQ.SystemMessages.Error;
using Options = Microsoft.Extensions.Options.Options;


namespace adas_core.Test.Services;

[TestFixture]
public class RecordingServiceTest
{
    /// <summary>
    /// Sets up the test environment for <see cref="RecordingService"/> by initializing configuration options, creating mock
    /// dependencies (logger, HTTP client factory, publisher, authentication, client message, subscribers, and patient services),
    /// and configuring the publisher mock to successfully send both regular messages and errors.
    /// </summary>
    [SetUp]
        public void Setup()
        {
            _optionsApiSettings = Options.Create(_apiSettings);
            _optionsRabbitMqSettings = Options.Create(_rabbitMqSettings);
            _optionsRecordingSettings = Options.Create(_recordingSettings);
            _publisherServiceMock = new Mock<IPublisherService>();
    
            _logger = new Mock<ILogger<RecordingService>>();
            _authServiceMock = new Mock<IAuthService>();
    
            _httpClientFactoryMock = new Mock<IHttpClientFactory>();
            _httpMessageHandlerMock = new Mock<HttpMessageHandler>();
            _clientMessageServiceMock = new Mock<IClientMessageService>();
            _subscribersServiceMock = new Mock<ISubscribersService>();
            _patientServiceMock = new Mock<Lazy<IPatientService>>();
    
            var client = new HttpClient(_httpMessageHandlerMock.Object);
    
            _httpClientFactoryMock.Setup(c => c.CreateClient(It.IsAny<string>())).Returns(client);
    
            _recordingService = new RecordingService(
                _optionsRabbitMqSettings,
                _optionsRecordingSettings,
                _logger.Object,
                _httpClientFactoryMock.Object,
                _publisherServiceMock.Object,
                _authServiceMock.Object,
                _optionsApiSettings,
                _clientMessageServiceMock.Object,
                _subscribersServiceMock.Object,
                _patientServiceMock.Object
            );
    
    
            // Set up the mock object to return a specific value when a method is called
            _publisherServiceMock.Setup(x => x.SendMessage(It.IsAny<object>(), It.IsAny<string>())).ReturnsAsync(true);
            _publisherServiceMock.Setup(x => x.SendMessageError(It.IsAny<Error>(), It.IsAny<string>())).ReturnsAsync(true);
        }

    private RecordingService _recordingService = null!;

    //Mock<PublisherService> mockSingleton = null!;
    private Mock<IHttpClientFactory> _httpClientFactoryMock = null!;
    private Mock<HttpMessageHandler> _httpMessageHandlerMock = null!;
    private Mock<IPublisherService> _publisherServiceMock = null!;
    private Mock<IAuthService> _authServiceMock = null!;
    private Mock<Lazy<IPatientService>> _patientServiceMock = null!;

    private readonly RabbitMqSettings _rabbitMqSettings = new()
    {
        RecordingQueue = "recordings"
    };

    private IOptions<RabbitMqSettings> _optionsRabbitMqSettings = null!;

    private readonly RecordingSettings _recordingSettings = new()
    {
        RecordingApiUrl = "http://localhost:8082"
    };

    private IOptions<RecordingSettings> _optionsRecordingSettings = null!;

    private readonly ApiSettings _apiSettings = new()
    {
        StartRecordingWithoutPatientNumber = false
    };

    private IOptions<ApiSettings> _optionsApiSettings = null!;

    private Mock<ILogger<RecordingService>> _logger = null!;
    private Mock<IClientMessageService> _clientMessageServiceMock = null!;
    private Mock<ISubscribersService> _subscribersServiceMock = null!;

    /// <summary>
    /// Verifies that <see cref="RecordingService.GetRecordings"/> returns an empty collection of <see cref="RecordingData"/> when the HTTP response indicates an unauthorized (401) status, ensuring the service correctly handles failed authentication scenarios by yielding no recordings rather than throwing or returning null.
    /// </summary>
    [Test]
        public async Task GetRecordings_Return_Empty()
        {
            var httpResponseMessage = new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.Unauthorized,
                Content = new StringContent("Content text.")
            };
    
            var recordingData = new List<RecordingData>();
    
            _authServiceMock.Setup(s => s.GetToken()).ReturnsAsync("abc");
    
            _httpMessageHandlerMock.Protected()
                .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(),
                    ItExpr.IsAny<CancellationToken>())
                .ReturnsAsync(httpResponseMessage);
    
            var result = await _recordingService.GetRecordings(4);
    
            Assert.That(result, Is.Not.Null);
            //Assert.AreEqual(result, recordingData);
            Assert.That(result, Is.EqualTo(recordingData));
        }

    /// <summary>
    /// Verifies that <see cref="RecordingService.GetRecordings"/> returns the expected <see cref="RecordingData"/> list
    /// when the HTTP endpoint responds with a successful payload containing a recording and its associated patient.
    /// Asserts that the returned items match the source data for status, room identifier, and patient identity fields.
    /// </summary>
    [Test]
        public async Task GetRecordings_Return_RecordingData()
        {
            var patient = new Patient
            {
                Id = "id",
                FirstName = "firstName",
                LastName = "lastName"
            };
    
            var recordingData = new List<RecordingData>
            {
                new()
                {
                    Patient = patient,
                    RoomId = 4,
                    Status = "INITIALIZED"
                }
            };
    
            var httpResponseMessage = new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.OK,
                Content = new StringContent(JsonConvert.SerializeObject(recordingData), Encoding.UTF8, "application/json")
            };
    
            _httpMessageHandlerMock.Protected()
                .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(),
                    ItExpr.IsAny<CancellationToken>())
                .ReturnsAsync(httpResponseMessage);
    
            _authServiceMock.Setup(s => s.GetToken()).ReturnsAsync("abc");
    
            var result = await _recordingService.GetRecordings(4);
    
            Assert.That(result, Is.Not.Null);
            using (Assert.EnterMultipleScope())     
            {
                Assert.That(recordingData[0].Status, Is.EqualTo(result![0].Status));
                Assert.That(recordingData[0].Patient?.Id, Is.EqualTo(result[0].Patient?.Id));
                Assert.That(recordingData[0].Patient?.LastName, Is.EqualTo(result[0].Patient?.LastName));
                Assert.That(recordingData[0].Patient?.FirstName, Is.EqualTo(result[0].Patient?.FirstName));
                Assert.That(recordingData[0].RoomId, Is.EqualTo(result[0].RoomId));
            }
        }

    /// <summary>
    /// Tests that calling <c>SaveRequest</c> on the recording service throws a <see cref="NotImplementedException"/> when supplied with an <see cref="ApiRequest"/>, using a mocked HTTP message handler that returns a successful response.
    /// </summary>
    [Test]
        public Task SaveRequest()
        {
            ApiRequest apiRequest = new();
    
            var httpResponseMessage = new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.OK,
                Content = new StringContent("Content text.")
            };
    
            _httpMessageHandlerMock.Protected()
                .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(),
                    ItExpr.IsAny<CancellationToken>())
                .ReturnsAsync(httpResponseMessage);
    
            Func<Task> act = async () => await _recordingService.SaveRequest(apiRequest);
            Assert.ThrowsAsync<NotImplementedException>(act);
            return Task.CompletedTask;
        }
}