using adas_core.Application.Repositories.Interfaces;
using adas_core.Domain.Models;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.GroupedObservations;
using adas_core.Domain.Models.MongoModels;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoUtils = adas_core.Infrastructure.Utils.MongoUtils;

namespace adas_core.Infrastructure.Repositories;

public class AlarmRepository : MongoRepository<PatientObservationAlarm>, IAlarmRepository
{
    private readonly ApiSettings _apiSettings;
    private readonly ILogger<AlarmRepository> _logger;

    public AlarmRepository(IOptions<ApiSettings> apiSettings, IMongoDatabase database, ILogger<AlarmRepository> logger)
        : base(database)
    {
        if (apiSettings == null) throw new ArgumentNullException(nameof(apiSettings));
        _logger = logger;
        _apiSettings = apiSettings.Value;
    } //For testing

    

    public async Task<List<PatientObservationAlarm>> AggregatedPatientLastObservationsByField(ObjectId patientId,
        List<Field>? filterObservations = null)
    {
        try
        {
            var results = new List<PatientObservationAlarm>();
            IAsyncCursor<PatientObservationAlarm>? cursor;
            var builder = Builders<PatientObservationAlarm>.Filter;

            if (filterObservations != null)
            {
                foreach (var obs in filterObservations)
                {
                    FilterDefinition<PatientObservationAlarm> filter;

                    if (obs is { OnlyExpired: true, Name: not null })
                        filter = builder.And(
                            builder.Eq(o => o.PatientId, patientId),
                            builder.Eq(o => o.Name, obs.Name),
                            builder.Eq("Expired", obs.OnlyExpired)
                            //builder.Eq(o => o.Expired, obs.OnlyExpired)
                        );
                    else
                        filter = builder.And(
                            builder.Eq(o => o.PatientId, patientId),
                            builder.Eq(o => o.Name, obs.Name)
                        );

                    cursor = await Collection.FindAsync(
                        filter,
                        new FindOptions<PatientObservationAlarm>
                        {
                            Sort = Builders<PatientObservationAlarm>.Sort.Descending("time").Descending("id"),
                            Limit = obs.Last
                        });

                    results.AddRange(cursor.ToEnumerable());
                }
            }
            else
            {
                var filter = builder.Eq(o => o.PatientId, patientId);

                cursor = await Collection.FindAsync(
                    filter,
                    new FindOptions<PatientObservationAlarm>
                        { Sort = Builders<PatientObservationAlarm>.Sort.Descending("time").Descending("id") });

                results.AddRange(cursor.ToEnumerable());
            }

            return results;
        }
        catch (Exception ex)
        {
            _logger.LogError("Error aggregated patient last observations by field {exMessage}", ex.Message);
            throw;
        }
    }

    public async Task<List<PatientObservationAlarm>> AggregatedPatientNotExpiredObservationsByField(
        ObjectId patientId,
        List<Field>? filterObservations,
        List<ConfigObservation> configAlarm)
    {
        try
        {
            var results = new List<PatientObservationAlarm>();
            IAsyncCursor<PatientObservationAlarm>? cursor;
            var builder = Builders<PatientObservationAlarm>.Filter;

            if (filterObservations != null)
            {
                foreach (var obs in filterObservations)
                {
                    FilterDefinition<PatientObservationAlarm> filter;
                    var conf = configAlarm.FirstOrDefault(c => c.Name == obs.Name);

                    if (obs is { OnlyExpired: true, Name: not null })
                        filter = builder.And(
                            builder.Eq(o => o.PatientId, patientId),
                            builder.Eq(o => o.Name, obs.Name),
                            builder.Eq("Expired", obs.OnlyExpired)
                            //builder.Eq(o => o.Expired, obs.OnlyExpired)
                        );
                    else
                        filter = builder.And(
                            builder.Eq(o => o.PatientId, patientId),
                            builder.Eq(o => o.Name, obs.Name)
                        );

                    if (conf is { Expires: not null })
                    {
                        var dateNow = DateTime.UtcNow.AddSeconds(conf.Expires.Value * -1);
                        filter = builder.And(
                            filter,
                            builder.Gte(o => o.Time, dateNow)
                        );
                    }

                    cursor = await Collection.FindAsync(
                        filter,
                        new FindOptions<PatientObservationAlarm>
                            { Sort = Builders<PatientObservationAlarm>.Sort.Descending("time").Descending("id") });

                    results.AddRange(cursor.ToEnumerable());
                }
            }
            else
            {
                var filter = builder.Eq(o => o.PatientId, patientId);

                cursor = await Collection.FindAsync(
                    filter,
                    new FindOptions<PatientObservationAlarm>
                        { Sort = Builders<PatientObservationAlarm>.Sort.Descending("time").Descending("id") });

                results.AddRange(cursor.ToEnumerable());
            }

            return results;
        }
        catch (Exception ex)
        {
            _logger.LogError("Error aggregated patient last observations by field {exMessage}", ex.Message);
            throw;
        }
    }

    public override string GetCollectionName()
    {
        return _apiSettings.PatientsAlarms ?? "patients_alarms";
    }

    public override async Task CreateIndexes()
    {
        try
        {
            var options = new CreateIndexOptions { Background = true, Unique = false };
            var indexes = new List<CreateIndexModel<PatientObservationAlarm>>
            {
                new("{ patientid: 1 }", options),
                new("{ patientid: 1, name: 1 }", options),
                new("{ name: 1 }", options),
                new("{ patientid: 1, name: 1 , time: 1}", options)
            };
            await MongoUtils.EnsureIndexes(Collection, indexes);
        }
        catch (Exception e)
        {
            _logger.LogError(
                "error creating indexes for observation collection {eMessage} TRACE: {eStackTrace}", e.Message,
                e.StackTrace);
            throw;
        }
    }
}