using adas_core.Application.Services.Interfaces;
using adas_core.Application.Subscriptions;
using adas_core.Domain.Enums;
using adas_core.Domain.Models;
using adas_core.Domain.Models.GroupedObservations;
using MongoDB.Bson;
using Moq;
using static adas_core.Domain.Models.GroupedObservation;

namespace adas_core.Test.Models.SignalR;

[TestFixture]
public class SubscriberGroupedTest
{
    /// <summary>
    /// Initializes test fixtures used by grouped observation and WebSocket subscriber tests, including
    /// <see cref="GroupedObservation"/>, <see cref="GroupedField"/>, <see cref="WsSubscriber"/>, and
    /// <see cref="WsSubscriberGrouped"/> instances configured for two distinct patients with shift- and
    /// hour-based regularities and last/last-filled result mappings.
    /// </summary>
    [SetUp]
        public void Setup()
        {
            // Set up GroupedObservation
            _go = new GroupedObservation
            {
                PatientId = _patient1,
                Name = "GroupedObservationTest1",
                Group = "GroupedObservationTest1",
                Observations = []
            };
            _go2 = new GroupedObservation
            {
                PatientId = _patient2,
                Name = "GroupedObservationTest2",
                Group = "GroupedObservationTest2",
                Observations = []
            };
    
            // Set up GroupedField
            _gf1 = new GroupedField
            {
                Name = "GroupedObservationTest1",
                Group = "GroupedFieldTest1",
                StartTimeShift = ["00:59", "04:59", "08:59", "12:59", "16:59", "20:59"],
                Max = 24,
                Regularity = GroupedObservationEnum.Regularity.Shift,
                Result = [GroupedObservationEnum.Result.Last]
            };
            _gf2 = new GroupedField
            {
                Names = ["GroupedObservationTest2", "GroupedObservationTest1"],
                Group = "GroupedFieldTest2",
                Max = 24,
                Regularity = GroupedObservationEnum.Regularity.Hour,
                Result = [GroupedObservationEnum.Result.Last, GroupedObservationEnum.Result.LastFilled]
            };
    
            // Set up WebSocketSubscriber
            _ws1 = new WsSubscriber("1234")
            {
                Id = "1234",
                Box = "Box1",
                Section = "Section1",
                Version = "1.0.0",
                GroupedFields = [],
                SubscriptionType = SubscriptionEnum.WsType.Box
            };
            _ws2 = new WsSubscriber("1234")
            {
                Id = "5321",
                Box = "Box2",
                Section = "Section1",
                Version = "1.0.0",
                GroupedFields = [],
                SubscriptionType = SubscriptionEnum.WsType.Box
            };
    
            // Set up groupedFields for subscribers
            _ws1.GroupedFields = [_gf1];
            _ws2.GroupedFields = [_gf1, _gf2];
    
            // Set up WsSubscriberGrouped
            _wsBase = new WsSubscriberGrouped(_patient1, _ws1.Id, "Romance Standard Time", _gf1, _go, delegate { });
            _wsBase2 = new WsSubscriberGrouped(_patient2, _ws2.Id, "Romance Standard Time", _gf2, _go2, delegate { });
        }

    private GroupedObservation _go;
    private GroupedObservation _go2;

    private GroupedField _gf1;
    private GroupedField _gf2;

    private WsSubscriber _ws1;
    private WsSubscriber _ws2;

    private WsSubscriberGrouped _wsBase;
    private WsSubscriberGrouped _wsBase2;

    private readonly ObjectId _patient1 = ObjectId.GenerateNewId();
    private readonly ObjectId _patient2 = ObjectId.GenerateNewId();

    /// <summary>
    /// Verifies that the <see cref="ISubscriberGroupedService.GetGrouped"/> method returns the list of grouped subscribers provided by the mocked service.
    /// Ensures the service correctly returns the expected collection of <see cref="WsSubscriberGrouped"/> items without modification.
    /// </summary>
    [Test]
        public void GetGroupeds_ReturnsExpectedResult()
        {
            var mockSubscriberGrouped = new List<WsSubscriberGrouped>
            {
                new(_patient1, _ws1.Id, "Romance Standard Time", _gf1, _go, delegate { }),
                new(_patient2, _ws1.Id, "Romance Standard Time", _gf1, _go, delegate { })
            };
    
            var mockSubscriberGroupedService = new Mock<ISubscriberGroupedService>();
            mockSubscriberGroupedService.Setup(x => x.GetGrouped()).Returns(mockSubscriberGrouped);
    
    
            var result = mockSubscriberGroupedService.Object.GetGrouped();
    
            Assert.That(result, Is.EqualTo(mockSubscriberGrouped));
        }

    /// <summary>
    /// Verifies the behavior of the <c>Compare</c> extension method by asserting that it returns <c>true</c> when the gender and patient identifiers match the active context, and <c>false</c> when they do not, covering both the primary and secondary contexts.
    /// </summary>
    [Test]
        public void Compare_ExtensionResult()
        {
            using (Assert.EnterMultipleScope())
            {
                Assert.That(_wsBase.Compare(_gf1, _go.PatientId), Is.True);
                Assert.That(_wsBase.Compare(_gf1, _go2.PatientId), Is.False);
                Assert.That(_wsBase.Compare(_gf2, _go.PatientId), Is.False);
                Assert.That(_wsBase2.Compare(_gf2, _go2.PatientId), Is.True);
            };
        }

    /// <summary>
    /// Verifies that a new patient observation is considered relevant for its group when it is the first observation recorded within the current hour.
    /// </summary>
    [Test]
        public void Observation_is_rrelevant_because_is_the_first_in_hour()
        {
            var obs = new PatientObservation
            {
                Time = DateTime.UtcNow,
                Value = 5
            };
    
    
            Assert.That(_wsBase.IsNewObservationRelevantForGroup(obs), Is.True);
        }

    /// <summary>
    /// Verifies that a new patient observation is considered relevant for a grouped observation when an observation already exists within the configured hour interval and the grouping uses the Max result.
    /// </summary>
    [Test]
        public void Grouped_Observation_Already_Observations_In_Hour_Should_Be_Relevant_By_Max_Result()
        {
            var patientId = ObjectId.GenerateNewId();
            var grouped = new GroupedObservation
            {
                PatientId = patientId,
                Name = "GroupedObservationTest1",
                Group = "GroupedObservationTest1",
                Observations =
                [
                    new GroupedObservationObs
                    {
                        Max = new GroupedObservationObsValue(5, DateTime.UtcNow)
                    }
                ]
            };
    
            var wsSubscriber = new WsSubscriber("1234")
            {
                Id = "1234",
                Box = "Box1",
                Section = "Section1",
                Version = "1.0.0",
                GroupedFields = [],
                SubscriptionType = SubscriptionEnum.WsType.Box
            };
    
            var groupedField = new GroupedField
            {
                Names = ["GroupedObservationTest2", "GroupedObservationTest1"],
                Group = "GroupedFieldTest2",
                Max = 24,
                Regularity = GroupedObservationEnum.Regularity.Hour,
                Result = [GroupedObservationEnum.Result.Max]
            };
    
            var newObservation = new PatientObservation
            {
                PatientId = patientId,
                Time = DateTime.UtcNow,
                Value = 8
            };
    
            var wsGrouped = new WsSubscriberGrouped(patientId, wsSubscriber.Id, "", groupedField, grouped, delegate { });
            //for utc.now two observations, last was 5 and now is 8 result is by Max, should be relevant
            Assert.That(wsGrouped.IsNewObservationRelevantForGroup(newObservation), Is.True);
        }

    /// <summary>
    /// Verifies that a new observation is considered not relevant for a grouped observation when the grouping rule is set to "Max", the regularity is "Hour", an observation already exists within the same hour, and the new observation's value does not exceed the existing maximum value.
    /// </summary>
    [Test]
        public void Grouped_Observation_Already_Observations_In_Hour_Should_Be_IRelevant_By_Max_Result()
        {
            var patientId = ObjectId.GenerateNewId();
            var grouped = new GroupedObservation
            {
                PatientId = patientId,
                Name = "GroupedObservationTest1",
                Group = "GroupedObservationTest1",
                Observations =
                [
                    new GroupedObservationObs
                    {
                        Max = new GroupedObservationObsValue(5, DateTime.UtcNow),
                        Time = DateTime.UtcNow
                    }
                ]
            };
    
            var wsSubscriber = new WsSubscriber("1234")
            {
                Id = "1234",
                Box = "Box1",
                Section = "Section1",
                Version = "1.0.0",
                GroupedFields = [],
                SubscriptionType = SubscriptionEnum.WsType.Box
            };
    
            var groupedField = new GroupedField
            {
                Names = ["GroupedObservationTest2", "GroupedObservationTest1"],
                Group = "GroupedFieldTest2",
                Max = 24,
                Regularity = GroupedObservationEnum.Regularity.Hour,
                Result = [GroupedObservationEnum.Result.Max]
            };
    
            var newObservation = new PatientObservation
            {
                PatientId = patientId,
                Time = DateTime.UtcNow,
                Value = 4
            };
    
            var wsGrouped = new WsSubscriberGrouped(patientId, wsSubscriber.Id, "", groupedField, grouped, delegate { });
            //for utc.now two observations, last was 5 and now is 4 result is by Max, should be Irelevant
            Assert.That(wsGrouped.IsNewObservationRelevantForGroup(newObservation), Is.False);
        }
}