using adas_core.Application.Repositories.Interfaces;
using adas_core.Application.Services.Interfaces;
using adas_core.Domain.Models;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.MongoModels;
using Microsoft.Extensions.Options;

namespace adas_core.Application.Services;

/// <summary>
/// Provides a proof-of-concept implementation of the IPoCMappingService interface,
/// delivering the mapping functionality defined by that contract.
/// </summary>
/// <!-- aidoc:v1 sig=47ed72e -->
public class PoCMappingService : IPoCMappingService
{
    private readonly string _key;
    private readonly bool _mappingRequired;
    private readonly IPoCMappingRepository _pocMappingRepository;
    private readonly int? _refreshTimeout;

    private PoCMapping? _mapping;
    private DateTime _nextRefresh = DateTime.MinValue;

    /// <summary>
    /// Initializes a new instance of the <see cref="PoCMappingService"/> class, storing the supplied repository and capturing point-of-care mapping configuration values from <paramref name="apiSettings"/>.
    /// </summary>
    /// <param name="pocMappingRepository">The <see cref="IPoCMappingRepository"/> used by the service to access mapping data.</param>
    /// <param name="apiSettings">The <see cref="IOptions{ApiSettings}"/> providing access to the point-of-care mapping settings.</param>
    /// <!-- aidoc:v1 sig=3865e0c body=4a11e38 -->
    public PoCMappingService(IPoCMappingRepository pocMappingRepository, IOptions<ApiSettings> apiSettings)
    {
        _pocMappingRepository = pocMappingRepository;
        _refreshTimeout = apiSettings.Value.PointOfCareMapping?.Refresh;
        _mappingRequired = apiSettings.Value.PointOfCareMapping?.Required ?? _mappingRequired;
        _key = apiSettings.Value.PointOfCareMapping?.Key ?? "PV1";
    }

    /// <summary>
    ///     Maps a received location with bed and pointOfCare to the equivalence in the PocMapping table if it finds the value,
    ///     otherwise it returns null
    /// </summary>
    /// <param name="original">location</param>
    /// <returns>mapped location</returns>
    /// <!-- aidoc-review:v1 severity=high kind=wrong_summary
    ///      "Summary states 'otherwise it returns null', but the code returns the original `PatientLocation` when `newBed` is empty and `_mappingRequired` is false, not always null." -->
    public async Task<PatientLocation?> Map(PatientLocation original)
    {
        var pocMapping = await GetMapping();
        var poc = pocMapping?.PointOfCares.FindAll(p => p.OriginalPoC == original.UnitName);
        var pocWithBed = poc?.FirstOrDefault(p => p.Beds.Any(bed => bed[0] == original.Bed));
        var newBed = pocWithBed?.Beds.Where(bed => bed[0] == original.Bed).Select(bed => bed[1]).FirstOrDefault();
        if (string.IsNullOrEmpty(newBed)) return _mappingRequired ? null : original;
        return new PatientLocation(pocWithBed!.NewPoC, newBed);
    }

    /// <summary>
    ///     Return mapping from cache of database based on refreshTime
    /// </summary>
    /// <returns></returns>
    /// <!-- aidoc-review:v1 severity=medium kind=missing_returns
    ///      "The <returns> tag is empty and does not describe that the method returns a Task<PoCMapping?> (the cached or freshly fetched mapping, or null)." -->
    private async Task<PoCMapping?> GetMapping()
    {
        if (_mapping == null || DateTime.Now > _nextRefresh)
        {
            _mapping = await _pocMappingRepository.FindByKey(_key);
            _nextRefresh = _refreshTimeout.HasValue
                ? DateTime.Now.AddSeconds(_refreshTimeout.Value)
                : DateTime.MaxValue;
        }

        return _mapping;
    }
}