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;

/// <summary>
/// Represents a MongoDB-backed repository for <see cref="PumpAlarmEvent"/> documents, exposing pump alarm event-specific data access through the <see cref="IPumpAlarmEventRepository"/> contract.
/// </summary>
/// <remarks>
/// Inherits the generic MongoDB persistence capabilities of <see cref="MongoRepository{TDocument}"/>, specializing them for the <see cref="PumpAlarmEvent"/> entity type.
/// </remarks>
/// <!-- aidoc:v1 sig=bf52831 -->
public class PumpAlarmEventRepository : MongoRepository<PumpAlarmEvent>, IPumpAlarmEventRepository
{
    private readonly ApiSettings _apiSettings;

    /// <summary>
    /// Initializes a new instance of the <see cref="PumpAlarmEventRepository"/> repository, capturing the configured <see cref="ApiSettings"/> from <paramref name="apiSettings"/> and forwarding <paramref name="database"/> to the base repository for persistence operations.
    /// </summary>
    /// <param name="apiSettings">The <see cref="IOptions{ApiSettings}"/> wrapper that exposes the application's <see cref="ApiSettings"/>.</param>
    /// <param name="database">The <see cref="IMongoDatabase"/> passed to the base constructor and used to perform MongoDB operations.</param>
    /// <!-- aidoc:v1 sig=cfddddb body=12fddac -->
    public PumpAlarmEventRepository(IOptions<ApiSettings> apiSettings, IMongoDatabase database)
        : base(database)
    {
        _apiSettings = apiSettings.Value;
    }

 

    /// <summary>
    /// Retrieves the collection name used for pump alarm events, returning the configured value from _apiSettings.PumpAlarmEvent when set, or falling back to the default "pump_alarm_event".
    /// </summary>
    /// <returns>The configured pump alarm event collection name, or the default "pump_alarm_event" when no configuration value is available.</returns>
    /// <!-- aidoc:v1 sig=94e22ff body=9e4a133 -->
    public override string GetCollectionName()
    {
        return _apiSettings.PumpAlarmEvent ?? "pump_alarm_event";
    }

    /// <summary>
    /// Creates the MongoDB indexes for the PumpAlarmEvent collection, optimizing the most common query patterns: device lookup with reverse-chronological time ordering, time-only range queries, patient lookup, and device queries filtered by alarm type.
    /// </summary>
    /// <!-- aidoc:v1 sig=4955da2 body=fae571b -->
    public override async Task CreateIndexes()
    {
        var indexModels = new List<CreateIndexModel<PumpAlarmEvent>>
        {
            // Principal para consultas por bomba y orden temporal
            new(
                Builders<PumpAlarmEvent>.IndexKeys
                    .Ascending(x => x.DeviceId)
                    .Descending(x => x.Time),
                new CreateIndexOptions { Name = "ix_deviceId_time" }),

            // Índice temporal
            new(
                Builders<PumpAlarmEvent>.IndexKeys.Descending(x => x.Time),
                new CreateIndexOptions { Name = "ix_time" }),

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

            //por tipo de alarma dentro de una bomba
            new(
                Builders<PumpAlarmEvent>.IndexKeys
                    .Ascending(x => x.DeviceId)
                    .Ascending(x => x.AlarmType),
                new CreateIndexOptions { Name = "ix_device_alarmType" })
        };

        await Collection.Indexes.CreateManyAsync(indexModels);
    }


    /// <summary>
    /// Asynchronously inserts a new <see cref="PumpAlarmEvent"/> document into the underlying MongoDB collection.
    /// </summary>
    /// <param name="alarmEvent">The <see cref="PumpAlarmEvent"/> record to persist.</param>
    /// <!-- aidoc:v1 sig=5de5fec body=d69d42c -->
    public async Task InsertAsync(PumpAlarmEvent alarmEvent)
    {
        await Collection.InsertOneAsync(alarmEvent);
    }

    /// <summary>
    /// Asynchronously retrieves <see cref="PumpAlarmEvent"/> records for the specified device, optionally constrained by an inclusive time range and a maximum result count.
    /// </summary>
    /// <param name="deviceId">The identifier of the device whose alarm events are queried.</param>
    /// <param name="from">Optional inclusive lower bound on the event time; when <see langword="null"/>, no lower bound is applied.</param>
    /// <param name="to">Optional inclusive upper bound on the event time; when <see langword="null"/>, no upper bound is applied.</param>
    /// <param name="limit">Optional maximum number of events to return; when <see langword="null"/>, all matching events are returned.</param>
    /// <returns>A task producing the matching <see cref="PumpAlarmEvent"/> records ordered by time in descending order.</returns>
    /// <!-- aidoc:v1 sig=ea193a8 body=0e52ba3 -->
    public async Task<IEnumerable<PumpAlarmEvent>> FindByDeviceIdAsync(string deviceId, DateTime? from = null,
        DateTime? to = null, int? limit = null)
    {
        var filter = Builders<PumpAlarmEvent>.Filter.Eq(x => x.DeviceId, deviceId);

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

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

        var find = Collection.Find(filter).SortByDescending(x => x.Time);
        if (limit.HasValue) find = find.Limit(limit.Value) as IOrderedFindFluent<PumpAlarmEvent, PumpAlarmEvent>;

        return await find.ToListAsync();
    }

    /// <summary>
    /// Asynchronously retrieves the most recent <see cref="PumpAlarmEvent"/> for the device identified by <paramref name="deviceId"/>, returning the entry with the latest time stamp, or <c>null</c> if no event exists.
    /// </summary>
    /// <param name="deviceId">The identifier of the device whose latest alarm event is being retrieved.</param>
    /// <returns>A <see cref="Task{PumpAlarmEvent}"/> that yields the latest <see cref="PumpAlarmEvent"/> associated with <paramref name="deviceId"/>, or <c>null</c> if no matching event is found.</returns>
    /// <!-- aidoc:v1 sig=f0fbfb3 body=088b34a -->
    /// <!-- aidoc-review:v1 severity=low kind=wrong_returns
    ///      "The <returns> tag references Task{PumpAlarmEvent} without the nullable annotation, while the actual return type is Task<PumpAlarmEvent?> (though the description text does mention nullability)." -->
    public async Task<PumpAlarmEvent?> FindLastByDeviceIdAsync(string deviceId)
    {
        return await Collection
            .Find(x => x.DeviceId == deviceId)
            .SortByDescending(x => x.Time)
            .FirstOrDefaultAsync();
    }

    /// <summary>
    /// Asynchronously removes all documents linked to the specified patient by deleting every record whose PatientId matches the supplied <paramref name="patientId"/>.
    /// </summary>
    /// <param name="patientId">The <see cref="ObjectId"/> of the patient whose associated documents should be deleted.</param>
    /// <!-- aidoc:v1 sig=4776e51 body=395df12 -->
    public async Task DeleteByPatientId(ObjectId patientId)
    {
        await Collection.DeleteManyAsync(x => x.PatientId == patientId);
    }
        
        
    /// <summary>
    /// Asynchronously updates the value of the specified field across multiple <see cref="PumpAlarmEvent"/> documents, replacing occurrences of <paramref name="oldId"/> with <paramref name="newId"/>, and returns the number of documents that were modified.
    /// </summary>
    /// <param name="fieldName">The name of the <see cref="MongoDB.Bson.ObjectId"/> field on <see cref="PumpAlarmEvent"/> whose value should be replaced.</param>
    /// <param name="newId">The new <see cref="MongoDB.Bson.ObjectId"/> value to assign to the field in matching documents.</param>
    /// <param name="oldId">The existing <see cref="MongoDB.Bson.ObjectId"/> value used to identify documents to be updated.</param>
    /// <returns>The number of <see cref="PumpAlarmEvent"/> documents modified by the bulk update.</returns>
    /// <!-- aidoc:v1 sig=207e960 body=64489e6 -->
    public async Task<long> UpdateManyObjectIdByFiledNameAsync(string fieldName, ObjectId newId, ObjectId oldId)
    {
        var filter = Builders<PumpAlarmEvent>.Filter.Eq(fieldName, oldId);
        var update = Builders<PumpAlarmEvent>.Update.Set(fieldName, newId);
        var result = await Collection.UpdateManyAsync(filter, update);
        return result.ModifiedCount;
    }

}