using adas_core.Application.Exceptions;
using adas_core.Application.Services.Interfaces;
using adas_core.Authentication.Attributes;
using adas_core.Domain.Enums;
using adas_core.Domain.Models;
using adas_core.Domain.Models.DTO;
using adas_core.Domain.Models.Filter;
using adas_core.Domain.Models.Observations;
using adas_core.Logging;
using Microsoft.AspNetCore.Mvc;
using MongoDB.Bson;

namespace adas_core.Controllers;

[Route("observations")]
[ApiController]
public class ObservationController : ControllerBase
{
    //private readonly IConfigObservationService _configObservation;
    private readonly IObservationService _observationService;
    private readonly IPatientService _patientService;


    public ObservationController(IObservationService observationService, IPatientService patientService)
    {
        LogExecutionContext.TrySetTraceIdentifier(Guid.NewGuid().ToString(), true);

        _observationService = observationService;

        _patientService = patientService;
    }


    /// <summary>
    /// Processes an incoming observation request by mapping the observation DTO to an API request and persisting it either synchronously or asynchronously based on the wait result flag.
    /// </summary>
    /// <param name="observationDto">The observation data transfer object containing the patient number, location, observation data, observations, message time, and wait result used to build the API request.</param>
    /// <returns>An <see cref="IActionResult"/> representing an HTTP 204 No Content response after the request is handled.</returns>
    [HttpPost]
    [Route("")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthAdmin)]
    public IActionResult ProcessRequest([FromBody] ObservationDto observationDto)
    {
        var request = new ApiRequest
        {
            TraceId = Guid.NewGuid().ToString(),
            Type = "ORU_R01",
            PatientNumber = observationDto.PatientNumber,
            Location = observationDto.Location,
            ObservationData = observationDto.ObservationData,
            Observations = observationDto.Observations,
            MessageTime = observationDto.MessageTime,
            WaitResult = observationDto.WaitResult
        };


        if (request.WaitResult) _observationService.SaveRequest(request);

        else _observationService.SaveRequestAsync(request);
        return NoContent();
    }

    /// <summary>
    /// Processes nurse observations by constructing an API request and persisting it asynchronously.
    /// </summary>
    /// <param name="observationDto">The observation data received in the request body, including patient, location, and measurement details.</param>
    /// <returns>An <see cref="IActionResult"/> representing a 204 No Content response after the request is queued for saving.</returns>
    [HttpPost]
    [Route("NurseObservations")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthAdmin)]
    public IActionResult ProcessRequestObsNurse([FromBody] ObservationDto observationDto)
    {
        //if (request == null) return BadRequest("Request is null");
        var request = new ApiRequest
        {
            TraceId = Guid.NewGuid().ToString(),
            Type = "ORU_R01",
            PatientNumber = observationDto.PatientNumber,
            Location = observationDto.Location,
            ObservationData = observationDto.ObservationData,
            Observations = observationDto.Observations,
            MessageTime = observationDto.MessageTime,
            WaitResult = observationDto.WaitResult
        };

        _observationService.SaveRequestNurseObsAsync(request);
        return NoContent();
    }

    // TESTING: Dejar comentado al terminar las pruebas
    /// <summary>
    /// Processes an incoming API request, persisting it either synchronously or asynchronously
    /// based on the <see cref="ApiRequest.WaitResult"/> flag. Requires the caller to have the
    /// <see cref="PermissionEnum.RolesType.AuthAdmin"/> role.
    /// </summary>
    /// <param name="apiRequest">The API request payload received from the request body. When
    /// <c>WaitResult</c> is <c>true</c>, the request is saved synchronously; otherwise it is
    /// saved asynchronously.</param>
    /// <returns>An <see cref="IActionResult"/> representing an HTTP 200 OK response.</returns>
    [HttpPost]
    [Route("apiRequest")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthAdmin)]
    public IActionResult ProcessRequest([FromBody] ApiRequest apiRequest)
    {
        //if (request == null) return BadRequest("Request is null");

        if (apiRequest.WaitResult) _observationService.SaveRequest(apiRequest);

        else _observationService.SaveRequestAsync(apiRequest);
        return Ok();
    }

    /// <summary>
    /// Expires all observations by invoking the observation service's expiration process.
    /// </summary>
    /// <returns>An <see cref="IActionResult"/> representing the HTTP 200 OK response indicating a successful expiration operation.</returns>
    [HttpPost("expired")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public IActionResult ExpireAllExpired()
    {
        _observationService.ExpireObservations();
        return Ok();
    }


    /// <summary>
    /// Retrieves a paginated list of observations based on the supplied pagination filter.
    /// </summary>
    /// <param name="request">The pagination filter containing the paging criteria sent in the request body.</param>
    /// <returns>An <see cref="IActionResult"/> containing the paginated observations returned by the observation service.</returns>
    /// <exception cref="BadRequestException">Thrown when the <paramref name="request"/> is null.</exception>
    [HttpPost("paginated")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetPaginatedObservations([FromBody] PaginationFilter request)
    {
        if (request == null) throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestMissingParameters);
        var result = await _observationService.GetPaginatedObservations(request);
        return Ok(result);
    }

    /// <summary>
    /// Retrieves observations for a patient within a specified time range, falling back to archived records when the patient is not found in the active set.
    /// </summary>
    /// <param name="request">The request payload containing the patient number, optional start and end times, and optional observation names to filter by.</param>
    /// <returns>An <see cref="IActionResult"/> containing the observations found between the specified dates for the resolved patient.</returns>
    /// <exception cref="BadRequestException">Thrown when the request does not include a patient number.</exception>
    /// <exception cref="NotFoundException">Thrown when no patient matching the supplied patient number exists in either the active or archived records.</exception>
    [HttpGet]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetObservations([FromBody] ObservationsRequest request)
    {
        if (request.PatientNumber == null)
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestMissingParameters);

        var archivedObservation = false;
        var patient = await _patientService.FindByPatientNumber(request.PatientNumber);
        if (patient == null)
        {
            archivedObservation = true;
            patient = await _patientService.FindByPatientNumberArchived(request.PatientNumber);
        }

        if (patient == null) throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
        //if (request.ObservationNames?.Count == 0) return BadRequest("The request need almost one type");

        return Ok(await _observationService.FindAllBetweenDates(patient.Id, request.StartTime, request.EndTime,
            request.ObservationNames, archivedObservation));
    }

    /// <summary>
    /// Forcefully records a NEWS (National Early Warning Score) observation for a patient with a specified value.
    /// </summary>
    /// <param name="patientid">The unique identifier of the patient for whom the observation is being recorded.</param>
    /// <param name="value">The NEWS score value to associate with the observation.</param>
    /// <exception cref="BadRequestException">Thrown when the <paramref name="patientid"/> is not a valid ObjectId format or when the <paramref name="value"/> is not a valid integer.</exception>
    [HttpPost("{patientId}/force-news/{value}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> ForceNews(string patientid, string value)
    {
        var parsed = ObjectId.TryParse(patientid, out var paObjectId);
        if (!parsed) throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        if (!int.TryParse(value, out _)) throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        var patient = await _patientService.FindById(paObjectId);

        var newObs = new PatientObservation
        {
            Name = "NEWS",
            CodingSystem = "ADAS",
            Expires = 15,
            Value = value,
            PatientId = patient!.Id,
            Time = DateTime.Now
        };
        await _observationService.InsertObservation(newObs);

        return Ok();
    }
}