using adas_core.Application.Repositories.Interfaces;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.Pumps;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;

namespace adas_core.Infrastructure.Repositories
{
    public class PumpObservationRepository : MongoRepository<PumpObservation>, IPumpObservationRepository
    {
        private readonly ApiSettings _apiSettings;

     

        public PumpObservationRepository(IOptions<ApiSettings> apiSettings, IMongoDatabase database) : base(database)
        {
            _apiSettings = apiSettings.Value;
        }

        public override string GetCollectionName()
        {
            return _apiSettings.PumpObservations ?? "pump_observations";
        }

        public override async Task CreateIndexes()
        {
            var indexModels = new List<CreateIndexModel<PumpObservation>>
            {
                // Timeline por bomba (consulta más frecuente)
                new CreateIndexModel<PumpObservation>(
                    Builders<PumpObservation>.IndexKeys
                        .Ascending(x => x.DeviceId)
                        .Descending(x => x.Time),
                    new CreateIndexOptions { Name = "ix_deviceId_time" }),

                // consultas por paciente
                new CreateIndexModel<PumpObservation>(
                    Builders<PumpObservation>.IndexKeys.Ascending(x => x.PatientId),
                    new CreateIndexOptions { Name = "ix_patientId" }),

                
                // TTL por antigüedad (si aplica retención directa en Mongo)
                // new CreateIndexModel<PumpObservation>(
                //     Builders<PumpObservation>.IndexKeys.Ascending(x => x.Time),
                //     new CreateIndexOptions { Name = "ttl_time", ExpireAfter = TimeSpan.FromDays(180) })
            };

            await Collection.Indexes.CreateManyAsync(indexModels);
        }

        public async Task InsertAsync(PumpObservation obs)
        {
            await Collection.InsertOneAsync(obs);
        }

        public async Task InsertManyAsync(IEnumerable<PumpObservation>? observations)
        {
            if (observations == null) return;

            var list = observations as IList<PumpObservation> ?? observations.ToList();
            if (list.Count == 0) return;

            await Collection.InsertManyAsync(list);
        }

        public async Task<IEnumerable<PumpObservation>> FindByDeviceIdAsync(
            string deviceId,
            DateTime? from = null,
            DateTime? to = null,
            int? limit = null)
        {
            var filter = Builders<PumpObservation>.Filter.Eq(x => x.DeviceId, deviceId);

            if (from.HasValue)
                filter &= Builders<PumpObservation>.Filter.Gte(x => x.Time, from.Value);

            if (to.HasValue)
                filter &= Builders<PumpObservation>.Filter.Lte(x => x.Time, to.Value);

            var query = Collection.Find(filter)
                                  .SortByDescending(x => x.Time);

            if (limit.HasValue)
                query = query.Limit(limit.Value) as IOrderedFindFluent<PumpObservation, PumpObservation>;

            return await query.ToListAsync();
        }

        // histórico por paciente
        public async Task<IEnumerable<PumpObservation>> FindByPatientAsync(
            ObjectId patientId,
            DateTime? from = null,
            DateTime? to = null,
            int? limit = null)
        {
            var filter = Builders<PumpObservation>.Filter.Eq(x => x.PatientId, patientId);

            if (from.HasValue)
                filter &= Builders<PumpObservation>.Filter.Gte(x => x.Time, from.Value);

            if (to.HasValue)
                filter &= Builders<PumpObservation>.Filter.Lte(x => x.Time, to.Value);

            var query = Collection.Find(filter)
                                  .SortByDescending(x => x.Time);

            if (limit.HasValue)
                query = query.Limit(limit.Value) as IOrderedFindFluent<PumpObservation, PumpObservation>;

            return await query.ToListAsync();
        }
        
        public async Task<Dictionary<ObjectId, DateTime>> FindAllLastPatientObservationTimeAsync()
        {
            // Pipeline:
            // 1) Filtrar observaciones con PatientId no nulo
            // 2) Agrupar por PatientId
            // 3) Obtener el máximo Time
            // 4) Devolver diccionario

            var pipeline = new[]
            {
                new BsonDocument("$match", new BsonDocument
                {
                    { "patientid", new BsonDocument("$ne", BsonNull.Value) }
                }),
                new BsonDocument("$group", new BsonDocument
                {
                    { "_id", "$patientid" },
                    { "LastTime", new BsonDocument("$max", "$time") }
                })
            };

            var docs = await Collection.Aggregate<BsonDocument>(pipeline).ToListAsync();

            var result = new Dictionary<ObjectId, DateTime>();

            foreach (var doc in docs.Where(doc => 
                         doc["_id"].IsObjectId && doc["LastTime"].IsValidDateTime
                         ))
            {
                result[doc["_id"].AsObjectId] = doc["LastTime"].ToUniversalTime();
            }

            return result;
        }

        public async Task<PumpObservation?> FindLastByDeviceIdAsync(string deviceId)
        {
            return await Collection
                .Find(x => x.DeviceId == deviceId)
                .SortByDescending(x => x.Time)
                .FirstOrDefaultAsync();
        }

        public async Task<IEnumerable<PumpObservation>> FindByPatientId(ObjectId? patientId)
        {
            return await Collection.Find(x => x.PatientId == patientId).ToListAsync();
        }

        
        public async Task<List<PumpObservation>> AggregatedPatientLastObservations(ObjectId patientId, int num = 100)
        {
            var filterBuilder = Builders<PumpObservation>.Filter;
            var sortBuilder = Builders<PumpObservation>.Sort;

            var filter = filterBuilder.Eq(o => o.PatientId, patientId);
            var sort = sortBuilder.Descending("time");
            var options = new FindOptions<PumpObservation> { Sort = sort, Limit = num };

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

            var distinctObservations = observations.DistinctBy(m => new { m.Code, m.Name }).ToList();
            var sortedObservations = distinctObservations.OrderByDescending(x => x.Time).ToList();

            return sortedObservations;
        }
        
        public async Task DeleteByPatientId(ObjectId? patientId)
        {
            await Collection.DeleteManyAsync(x => x.PatientId == patientId);
        }
        public async Task<long> DeleteOlderThanDaysAsync(int days, string? name = null)
        {
            var limitDate = DateTime.UtcNow.AddDays(-days);

            var filter = Builders<PumpObservation>.Filter.Lt(x => x.Time, limitDate);
            if(!string.IsNullOrEmpty(name))
                filter &= Builders<PumpObservation>.Filter.Eq(x=> x.Name, name);

            return (await Collection.DeleteManyAsync(filter)).DeletedCount;
        }

        public async Task<long> DeleteKeepLastNAsync(int maxCount)
        {
            // Para cada DeviceId:
            var deviceIds = await Collection
                .Distinct<string>("DeviceId", FilterDefinition<PumpObservation>.Empty)
                .ToListAsync();

            long totalDeleted = 0;

            foreach (var filter in deviceIds.Select(deviceId => 
                         Builders<PumpObservation>.Filter.Eq(x => x.DeviceId, deviceId)
                         ))
            {
                var all = await Collection.Find(filter)
                    .SortByDescending(x => x.Time)
                    .ToListAsync();

                if (all.Count <= maxCount) 
                    continue;

                var toDelete = all.Skip(maxCount).Select(x => x.Id).ToList();

                var deleteFilter = Builders<PumpObservation>.Filter.In(x => x.Id, toDelete);
                var result = await Collection.DeleteManyAsync(deleteFilter);

                totalDeleted += result.DeletedCount;
            }

            return totalDeleted;
        }
        
        public async Task<long> UpdateManyObjectIdByFieldAsync(string fieldName, ObjectId newId, ObjectId? oldId)
        {
            if (string.IsNullOrWhiteSpace(fieldName))
                throw new ArgumentException("fieldName can't be null or empty.", nameof(fieldName));

            // Normalize casing (Mongo is case-sensitive)
            // if (fieldName.Equals("patientid", StringComparison.OrdinalIgnoreCase))
            //   fieldName = nameof(PumpObservation.PatientId);

            var filter = Builders<PumpObservation>.Filter.Eq(fieldName, oldId);
            var update = Builders<PumpObservation>.Update.Set(fieldName, newId);

            var result = await Collection.UpdateManyAsync(filter, update);
            return result.ModifiedCount;
        }


        public async Task<long> DeleteOlderNumberAsync(string name, int maxCount)
        {
            if (string.IsNullOrWhiteSpace(name))
                return 0;

            // 1. Filtrar todas las observaciones con ese Name
            var filter = Builders<PumpObservation>.Filter.Eq(x => x.Name, name);

            // 2. Obtenerlas ordenadas por Time DESC (las más recientes primero)
            var all = await Collection
                .Find(filter)
                .SortByDescending(x => x.Time)
                .ToListAsync();

            // 3. Si hay menos o igual al número permitido → no borrar nada
            if (all.Count <= maxCount)
                return 0;

            // 4. Seleccionar TODAS excepto las maxCount más recientes
            var toDeleteIds = all
                .Skip(maxCount)
                .Select(x => x.Id)
                .ToList();

            if (toDeleteIds.Count == 0)
                return 0;

            // 5. Borrar las seleccionadas
            var deleteFilter = Builders<PumpObservation>.Filter.In(x => x.Id, toDeleteIds);
            var result = await Collection.DeleteManyAsync(deleteFilter);

            return result.DeletedCount;
        }


        public async Task<List<PumpObservation>> FindLastObservations(ObjectId patientId, string name)
        {
            if (string.IsNullOrWhiteSpace(name))
                return [];

            var filter = Builders<PumpObservation>.Filter.And(
                Builders<PumpObservation>.Filter.Eq(x => x.PatientId, patientId),
                Builders<PumpObservation>.Filter.Eq(x => x.Name, name)
            );

            return await Collection
                .Find(filter)
                .SortByDescending(x => x.Time)
                .Limit(2)               
                .ToListAsync();
        }
       
    }
}