using System.Diagnostics;
using adas_core.Application.Repositories.Interfaces;
using adas_core.Domain.Models;
using adas_core.Domain.Models.AppSettings;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Driver;
using MongoUtils = adas_core.Infrastructure.Utils.MongoUtils;

namespace adas_core.Infrastructure.Repositories;

public class RecordingAlertRepository : MongoRepository<PatientRecordingAlert>, IRecordingAlertRepository
{
    private readonly ApiSettings _apiSettings;

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

   

    public override string GetCollectionName()
    {
        return _apiSettings.PatientsRecordingAlerts ?? "patients_recordingalerts";
    }

    public async Task<List<PatientRecordingAlert>> AggregatedPatientLastObservations(ObjectId patientId, int num)
    {
        var match = new BsonDocument
        {
            { "patientid", patientId }
        };
        var pipeline = new BsonDocument[]
        {
            new()
            {
                {
                    "$match", match
                }
            },
            new()
            {
                {
                    "$sort", new BsonDocument
                    {
                        { "codingSystem", 1 },
                        { "code", 1 },
                        { "time", -1 }
                    }
                }
            },
            new()
            {
                {
                    "$group", new BsonDocument
                    {
                        {
                            "_id", new BsonDocument
                            {
                                // { "codingSystem", "$codingSystem" } ,
                                // { "code", "$code" } ,
                                { "name", "$name" }
                            }
                        },
                        {
                            "results", new BsonDocument
                            {
                                { "$push", "$$ROOT" }
                            }
                        }
                    }
                }
            },
            new()
            {
                {
                    "$project", new BsonDocument
                    {
                        {
                            "results", new BsonDocument
                            {
                                { "$slice", new BsonArray { "$results", num } }
                            }
                        }
                    }
                }
            }
        };
        Debug.WriteLine(pipeline.ToJson());
        var result =
            await Collection.AggregateAsync<BsonDocument>(pipeline, new AggregateOptions { AllowDiskUse = true });
        var obs = new List<PatientRecordingAlert>();
        result.ToList().ForEach(it =>
        {
            foreach (var obit in it.GetValue("results").AsBsonArray)
            {
                var obsit = obit.AsBsonDocument;
                var pobs = BsonSerializer.Deserialize<PatientRecordingAlert>(obsit);
                pobs.PatientId = patientId;
                obs.Add(pobs);
            }
        });
        return obs;
    }

    public new async Task DeleteAsync(ObjectId id)
    {
        var filter = Builders<PatientRecordingAlert>.Filter.Eq(obs => obs.Id, id);
        await Collection.DeleteOneAsync(filter);
    }


    public override async Task InsertOneAsync(PatientRecordingAlert patientRecordingAlert)
    {
        await Collection.InsertOneAsync(patientRecordingAlert);
    }

    public async Task DeleteOlderDaysAsync(string name, int retentionPolicyValue)
    {
        var filterBuilder = Builders<PatientRecordingAlert>.Filter;
        var filter = filterBuilder.And(
            filterBuilder.Eq(obs => obs.Name, name),
            filterBuilder.Lt(obs => obs.Time, DateTime.UtcNow.AddDays(-1 * retentionPolicyValue))
        );

        await Collection.DeleteOneAsync(filter);
    }


    public async Task DeleteByPatientId(ObjectId patientId)
    {
        var filter = Builders<PatientRecordingAlert>.Filter.Eq(po => po.PatientId, patientId);
        await Collection.DeleteManyAsync(filter);
    }


    public async Task DeleteOlderNumberAsync(string name, int retentionPolicyValue)
    {
        var filterBuilder = Builders<PatientRecordingAlert>.Filter;
        var sortBuilder = Builders<PatientRecordingAlert>.Sort;

        var filter = filterBuilder.Eq(obs => obs.Name, name);
        var projection = Builders<PatientRecordingAlert>.Projection.Include(obs => obs.Id).Include(obs => obs.Time);
        var sort = sortBuilder.Descending("time");
        var options = new FindOptions<PatientRecordingAlert>
        {
            Projection = projection,
            Sort = sort,
            Skip = retentionPolicyValue
        };

        var result = await Collection.FindAsync(filter, options);

        await result.ForEachAsync(async obs =>
        {
            var idFilter = filterBuilder.Eq(ob => ob.Id, obs.Id);
            await Collection.DeleteOneAsync(idFilter);
        });
    }


    public async Task<IAsyncCursor<PatientRecordingAlert>> FindByPatientIdAsync(ObjectId patientId)
    {
        var filter = Builders<PatientRecordingAlert>.Filter.Eq(ob => ob.PatientId, patientId);
        return await Collection.FindAsync(filter);
    }


    public async Task<List<PatientRecordingAlert>> FindLastObservations(ObjectId patientId, string name, int num = 2)
    {
        var filterBuilder = Builders<PatientRecordingAlert>.Filter;
        var sortBuilder = Builders<PatientRecordingAlert>.Sort;

        var filter = filterBuilder.And(
            filterBuilder.Eq(ob => ob.PatientId, patientId),
            filterBuilder.Eq(ob => ob.Name, name)
        );

        var sort = sortBuilder.Descending("time");

        var options = new FindOptions<PatientRecordingAlert>
        {
            Sort = sort,
            Limit = num
        };

        var result = await Collection.FindAsync(filter, options);

        return await result.ToListAsync();
    }


    public async Task UpdateManyObjectId(string nameId, ObjectId id, ObjectId oldId)
    {
        await UpdateManyObjectIdAsync(nameId, id, oldId);
    }

    public override async Task CreateIndexes()
    {
        var options = new CreateIndexOptions { Background = true, Unique = false };
        var indexes = new List<CreateIndexModel<PatientRecordingAlert>>
        {
            new("{ patientid: 1 }", options)
        };

        await MongoUtils.EnsureIndexes(Collection, indexes);
    }
}