using adas_core.Application.Services.Interfaces;
using adas_core.Domain.Enums;
using adas_core.Domain.Models;
using adas_core.Domain.Models.Pumps;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;

namespace adas_core.Application.Customizations.BD;

/// <summary>
/// Represents a service that provides calculated observations, implementing the <see cref="ICalculatedObservations"/> interface.
/// </summary>
/// <remarks>
/// This type is instantiated with a primary constructor that accepts an <see cref="IServiceProvider"/> to resolve its dependencies.
/// </remarks>
public class CalculatedObservations(IServiceProvider serviceProvider) : ICalculatedObservations
{
    private readonly ILogger<CalculatedObservations> _logger =
        serviceProvider.GetRequiredService<ILogger<CalculatedObservations>>();

    private readonly Lazy<IObservationService> _observationService =
        serviceProvider.GetRequiredService<Lazy<IObservationService>>();

    /// <summary>
    /// Maps the specified patient observation by returning it unchanged within a completed task.
    /// The <paramref name="onlyByName"/> parameter is accepted to indicate whether the mapping should match observations by name only.
    /// </summary>
    /// <param name="obs">The patient observation to map.</param>
    /// <param name="onlyByName">Indicates whether the mapping should match observations by name only.</param>
    /// <returns>A <see cref="Task{T}"/> containing the provided observation.</returns>
    public Task<T?> Map<T>(T obs, bool onlyByName = false) where T : BasePatientObservation
    {
        return Task.FromResult(obs)!;
    }

    /// <summary>
    /// Returns the provided <see cref="PatientTreatment"/> as a completed task without modification.
    /// </summary>
    /// <param name="treatment">The patient treatment instance to map.</param>
    /// <returns>A completed <see cref="Task{PatientTreatment}"/> containing the provided treatment.</returns>
    public Task<PatientTreatment> Map(PatientTreatment treatment)
    {
        return Task.FromResult(treatment);
    }

    /// <summary>
    /// Maps the provided <see cref="PatientDiagnosis"/> instance to a completed <see cref="Task{TResult}"/>, returning the same diagnosis unchanged.
    /// </summary>
    /// <param name="diagnosis">The <see cref="PatientDiagnosis"/> instance to map.</param>
    /// <returns>A <see cref="Task{TResult}"/> containing the provided <see cref="PatientDiagnosis"/>.</returns>
    public Task<PatientDiagnosis> Map(PatientDiagnosis diagnosis)
    {
        return Task.FromResult(diagnosis);
    }

    /// <summary>
    /// Maps a pump observation, creating an associated alarm observation when the observation code is "IHE PCD-04".
    /// </summary>
    /// <param name="pumpObservation">The pump observation to be mapped.</param>
    /// <returns>The mapped pump observation.</returns>
    public async Task<PumpObservation> Map(PumpObservation pumpObservation)
    {
        if (pumpObservation.Code == "IHE PCD-04")
            await CreatePumpAlarmObservation(pumpObservation);
        return pumpObservation;
    }

    /// <summary>
    /// Calculates medicine observations for the specified patient using the provided list of active medicines.
    /// </summary>
    /// <param name="activeMedicines">The list of active medicines associated with the patient.</param>
    /// <param name="patientId">The unique identifier of the patient for whom the observations are calculated.</param>
    public Task CalculateMedicineObservation(List<Medicine> activeMedicines, ObjectId patientId)
    {
        return Task.CompletedTask;
    }

    /// <summary>
    /// Asynchronously calculates the active bolus for the specified patient.
    /// </summary>
    /// <param name="patientId">The unique identifier of the patient whose active bolus is being calculated.</param>
    /// <returns>A task that represents the asynchronous calculation of the active bolus.</returns>
    public Task CalculateActiveBolus(ObjectId patientId)
    {
        return Task.CompletedTask;
    }

    /// <summary>
    /// Retrieves the collection of active treatments associated with the specified patient identifier.
    /// </summary>
    /// <param name="id">The unique <see cref="ObjectId"/> identifier of the patient whose active treatments are being requested.</param>
    /// <returns>A task that represents the asynchronous operation, containing an <see cref="IEnumerable{PatientTreatment}"/> of nullable <see cref="PatientTreatment"/> entries for the patient's active treatments.</returns>
    /// <exception cref="NotImplementedException">Thrown to indicate that the method is not yet implemented.</exception>
    public Task<IEnumerable<PatientTreatment?>> GetActiveTreatmentsByPatient(ObjectId id)
    {
        throw new NotImplementedException();
    }

    /// <summary>
    /// Resolves time inconsistencies between the provided new patient observation and the previously recorded one, returning a corrected observation when applicable.
    /// </summary>
    /// <param name="newObservation">The new patient observation to be reconciled against the last stored observation.</param>
    /// <returns>A task that yields the time-corrected <see cref="PatientObservation"/>, or <c>null</c> when no correction is required or no prior observation exists.</returns>
    /// <exception cref="NotImplementedException">Thrown because the method has not been implemented yet.</exception>
    public Task<PatientObservation?> FixTimeInconsistencyWithLast(PatientObservation newObservation)
    {
        throw new NotImplementedException();
    }

    /// <summary>
    /// Performs a pre-mapping operation on the list of patient observations before further processing.
    /// In the base implementation, the list is returned unchanged, serving as a pass-through that may be overridden to apply custom transformations or validations.
    /// </summary>
    /// <param name="listToInsert">The list of patient observations to be pre-mapped before insertion.</param>
    /// <returns>A task containing the provided list of patient observations.</returns>
    public Task<List<PatientObservation>> PreMapList(List<PatientObservation> listToInsert)
    {
        return Task.FromResult(listToInsert);
    }

    /// <summary>
    /// Maps a source <see cref="PatientObservationAlarm"/> onto an existing <see cref="PatientObservation"/>, producing an updated observation that incorporates the alarm data.
    /// </summary>
    /// <param name="obs">The existing patient observation to which the alarm will be mapped.</param>
    /// <param name="alarmToInsert">The source alarm to be inserted/mapped into the observation.</param>
    /// <returns>A <see cref="Task{PatientObservation}"/> that represents the asynchronous mapping operation, returning the resulting patient observation.</returns>
    /// <exception cref="NotImplementedException">Thrown because the method has not been implemented yet.</exception>
    public Task<PatientObservation> MapSourceAlarm(PatientObservation obs, PatientObservationAlarm alarmToInsert)
    {
        throw new NotImplementedException();
    }

    /// <summary>
    /// Sends an alarm notification based on a patient observation, associated name, and optional alarm code.
    /// </summary>
    /// <param name="obs">The patient observation that triggers the alarm.</param>
    /// <param name="name">The name associated with the alarm.</param>
    /// <param name="code">The optional alarm code that categorizes the alarm; may be null.</param>
    /// <returns>A task that represents the asynchronous alarm sending operation.</returns>
    /// <exception cref="NotImplementedException">Thrown because the method is not yet implemented.</exception>
    public Task SendAlarm(PatientObservation obs, string name, AlarmEnum.Name? code)
    {
        throw new NotImplementedException();
    }

    /// <summary>
    /// Creates and inserts a patient observation representing a pump alarm event.
    /// The observation value is populated with the device identifier when available, otherwise the infusion identifier,
    /// and the patient identifier is only assigned if present on the source pump observation.
    /// Any exception raised while inserting the observation is logged and swallowed rather than rethrown.
    /// </summary>
    /// <param name="pumpObservation">The pump observation containing the alarm state, type, timestamps, and identifiers used to build the alarm patient observation.</param>
    /// <returns>A completed <see cref="Task"/> representing the insert operation, which never faults since exceptions are caught internally.</returns>
    private Task CreatePumpAlarmObservation(PumpObservation pumpObservation)
    {
        PatientObservation patientObservationAlarm = new()
        {
            CodingSystem = "ADAS_ALARM",
            Code = pumpObservation.AlarmState,
            Name = $"Alarm_Pump_{pumpObservation.AlarmType}",
            Value = pumpObservation.DeviceId != null
                ? $"Device Id: {pumpObservation.DeviceId}"
                : $"Infusion Id {pumpObservation.InfusionId}",
            Time = pumpObservation.Time
        };
            
        if (pumpObservation.PatientId.HasValue) patientObservationAlarm.PatientId = pumpObservation.PatientId.Value;
            
    
        //CheckAlarmConfig(patientObservationAlarm);
        try
        {
            _ = _observationService.Value.InsertObservation(patientObservationAlarm, mapObs: false);
        }
        catch (Exception e)
        {
            _logger.LogError("Error inserting Pump Observation Alarm. Exception: {e}", e);
        }
    
        return Task.CompletedTask;
    }
}