using adas_core.Application.Exceptions;
using adas_core.Application.Services.Interfaces;
using adas_core.Authentication.Attributes;
using adas_core.Authentication.Interfaces;
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.Masters;
using adas_core.Domain.Models.MongoModels;
using adas_core.Logging;
using Microsoft.AspNetCore.Mvc;
using MongoDB.Bson;

namespace adas_core.Controllers;

[Route("persons")]
[ApiController]
public class PatientController : ControllerBase
{
    private readonly IAdminPanelService _adminPanelService;
    private readonly IObservationService _observationService;
    private readonly IPatientService _patientService;
    private readonly IUnitService _unitService;
    private readonly IUserService _userService;


    /// <summary>
    /// Initializes a new instance of the <see cref="PatientController"/> class.
    /// </summary>
    /// <param name="patientService">The service responsible for managing patient-related operations.</param>
    /// <param name="observationService">The service responsible for managing patient observations.</param>
    /// <param name="adminPanelService">The service responsible for administrative panel operations on patients.</param>
    /// <param name="userService">The service responsible for user management and authentication.</param>
    /// <param name="unitService">The service responsible for unit-related operations.</param>

    public PatientController(
        IPatientService patientService,
        IObservationService observationService,
        IAdminPanelService adminPanelService,
        IUserService userService, IUnitService unitService)
    {
        LogExecutionContext.TrySetTraceIdentifier(Guid.NewGuid().ToString(), true);

        _observationService = observationService;
        _patientService = patientService;
        _adminPanelService = adminPanelService;
        _userService = userService;
        _unitService = unitService;
    }

    /// <summary>
    /// Retrieves all patient records, requiring the caller's role to include <see cref="PermissionEnum.RolesType.AuthSome"/>
    /// and a granted "DisplayId" permission on the "Source" resource.
    /// </summary>
    /// <returns>An <see cref="IActionResult"/> containing the full collection of patients retrieved from the patient service.</returns>
    [HttpGet]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetAllPatients()
    {
        return Ok(await _patientService.FindAll());
    }

    /// <summary>
    /// Retrieves a patient record by their unique patient number. Access is restricted to users with the AuthSome role.
    /// </summary>
    /// <param name="patientNumber">The unique patient number used to look up the patient.</param>
    /// <returns>An <see cref="IActionResult"/> containing the patient information returned by the service.</returns>
    [HttpGet("find-by-patientnumber/{patientNumber}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    public async Task<IActionResult> GetByPatientNumber(string patientNumber)
    {
        return Ok(await _patientService.FindByPatientNumber(patientNumber, true));
    }

    /// <summary>
    /// Retrieves the most recent observation for the specified patient.
    /// Validates the patient identifier format before delegating to the observation service to fetch the last record.
    /// </summary>
    /// <param name="patientId">The patient's identifier expected to be a valid ObjectId string.</param>
    /// <returns>An <see cref="IActionResult"/> containing the last observation payload on success.</returns>
    /// <exception cref="BadRequestException">Thrown when <paramref name="patientId"/> is not a valid ObjectId format.</exception>
    [HttpGet("{patientId}/last-observation")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetLastObservation(string patientId)
    {
        if (!ObjectId.TryParse(patientId, out var id))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        var obs = await _observationService.FindLastObservations(id, 1);
        return Ok(obs);
    }

    /// <summary>
    /// Retrieves the list of active patients for the PoC (Proof of Concept) workflow.
    /// Access is restricted to authorized users with the required role and source display permission.
    /// </summary>
    /// <returns>An <see cref="IActionResult"/> containing the collection of active patients returned by the service.</returns>
    [HttpGet("active-poc")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetActivePatients()
    {
        var patients = await _patientService.FindInActivePoC();
        return Ok(patients);
    }

    /// <summary>
    /// Retrieves a paginated list of patients based on the provided filter.
    /// Validates that the request is not null before delegating to the patient service.
    /// </summary>
    /// <param name="request">The pagination filter containing criteria for querying patients.</param>
    /// <returns>An <see cref="IActionResult"/> containing the paginated patients when successful.</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> GetPaginatedPatients([FromBody] PaginationFilter request)
    {
        if (request == null) throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestMissingParameters);
        var patients = await _patientService.GetPaginatedPatients(request);
        return Ok(patients);
    }

    /// <summary>
    /// Retrieves a basic list of all persons associated with registered patients, requiring the <c>AuthSome</c> role and the <c>Source.DisplayId</c> permission.
    /// </summary>
    /// <returns>An <see cref="IActionResult"/> containing an HTTP 200 response with the collection of person records derived from all patients.</returns>
    [HttpGet("basic")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetPatientsBasic()
    {
        var patients = await _patientService.FindAll();
        var persons = patients.Select(p => p.Person).ToList();
        return Ok(persons);
    }


    /// <summary>
    /// Processes an incoming patient request by constructing an ADT_A01 type API request with a new trace identifier, persisting it, and returning the updated patient record.
    /// </summary>
    /// <param name="patientDto">The patient data transfer object containing the patient number, location, patient details, message time, and wait result used to build the request.</param>
    /// <returns>An <see cref="IActionResult"/> containing the patient record retrieved after the request is saved.</returns>
    [HttpPost("request")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> ProcessRequest([FromBody] PatientDto patientDto)
    {
        var request = new ApiRequest
        {
            TraceId = Guid.NewGuid().ToString(),
            Type = "ADT_A01",
            PatientNumber = patientDto.PatientNumber,
            Location = patientDto.Location,
            Patient = patientDto.Patient,

            MessageTime = patientDto.MessageTime,
            WaitResult = patientDto.WaitResult,
            RequestFromPanel = true
        };
        await _patientService.SaveRequestAsync(request);
        var patient = await _patientService.FindByPatientNumber(patientDto.PatientNumber, true);

        return Ok(patient);
    }

    /// <summary>
    /// Processes an incoming API request and persists the associated patient request data.
    /// </summary>
    /// <param name="apiRequest">The API request payload received from the request body to be saved.</param>
    /// <returns>An <see cref="IActionResult"/> that returns HTTP 200 (OK) when the request is successfully persisted.</returns>
    [HttpPost("api-request")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> ProcessRequestApiRequest([FromBody] ApiRequest apiRequest)
    {
        await _patientService.SaveRequestAsync(apiRequest);
        return Ok();
    }

    /// <summary>
    /// Retrieves a patient by its unique identifier, validating the identifier format before delegating to the patient service.
    /// Requires the caller to have the appropriate role and source-level display permission.
    /// </summary>
    /// <param name="id">The patient's identifier expected to be a valid ObjectId string.</param>
    /// <returns>An <see cref="IActionResult"/> containing the patient data when found.</returns>
    /// <exception cref="BadRequestException">Thrown when <paramref name="id"/> is not a valid ObjectId format.</exception>
    [HttpGet("{id}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetPatientById(string id)
    {
        if (!ObjectId.TryParse(id, out var patientId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);

        var patient = await _patientService.FindById(patientId, true);
        return Ok(patient);
    }

    /// <summary>
    /// Exits (discharges) the patient identified by the provided id. Validates the patient and unit configuration,
    /// ensuring manual discharge is enabled before performing the operation.
    /// </summary>
    /// <param name="id">The string identifier of the patient to discharge; must be a valid ObjectId.</param>
    /// <returns>An <see cref="IActionResult"/> that returns HTTP 200 (OK) when the patient is successfully exited.</returns>
    /// <exception cref="BadRequestException">Thrown when <paramref name="id"/> is not a valid ObjectId format.</exception>
    /// <exception cref="NotFoundException">Thrown when no unit configuration is found for the patient's unit.</exception>
    /// <exception cref="ConflictException">Thrown when the unit operates in automatic census mode, disallowing manual discharge.</exception>
    [HttpPost("{id}/exit")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> ExitPatientById(string id)
    {
        if (!ObjectId.TryParse(id, out var patientId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        var patient = await _patientService.FindById(patientId);

        var unitConfig = await _unitService.FindById(patient!.UnitId);
        switch (unitConfig)
        {
            case null:
                throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
            case { Configuration.ManualDischarge: false }:
                throw new ConflictException(HttpEnum.ErrorMessage.ServiceUnavailableCensusInAutomaticMode);
            default:
                await _patientService.ExitPatientById(patientId);
                return Ok();
        }
    }

    /// <summary>
    /// Archives a patient procedure by removing it from the patient's procedure list and recording the change in the master list.
    /// Validates the caller identity, the format of the patient identifier, and applies a default description of "Deleted" when none is provided.
    /// </summary>
    /// <param name="id">The string representation of the patient identifier whose procedure will be archived.</param>
    /// <param name="options">The list of options describing the procedure to archive; the first entry is used to match the procedure name and to record the change.</param>
    /// <returns>An <see cref="IActionResult"/> containing the updated patient data when the operation succeeds.</returns>
    /// <exception cref="UnauthorizedException">Thrown when the current user identity name is not available in the HTTP context.</exception>
    /// <exception cref="BadRequestException">Thrown when the provided <paramref name="id"/> cannot be parsed as a valid identifier.</exception>
    [HttpPost("{id}/archived-procedure")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> ArchivePatientProcedure(string id, [FromBody] List<OptionList> options)
    {
        var userNameContext = HttpContext.User.Identity?.Name ??
                              throw new UnauthorizedException(HttpEnum.ErrorMessage.UnauthorizedNoPermission);

        var user = await _userService.GetUserByUserName(userNameContext);

        if (!ObjectId.TryParse(id, out var patientId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        var patient = await _patientService.FindById(patientId);
        var optionLists = patient!.Procedures?.Where(c => c.Name != options.First().Name).ToList();
        options.First().Description ??= "Deleted";
        var patientToReturn =
            await _patientService.UpdatePatientMasterList(patientId, MasterListType.ProcedureList, optionLists!, user,
                options);
        return Ok(patientToReturn);
    }

    /// <summary>
    /// Archives a patient test by removing it from the patient's test master list and ensuring the test description defaults to "Deleted" when not supplied.
    /// </summary>
    /// <param name="id">The unique identifier of the patient whose test is being archived.</param>
    /// <param name="options">The option entries describing the test to archive; the first entry's name identifies the test and its description is set to "Deleted" if null.</param>
    /// <returns>An <see cref="IActionResult"/> containing the updated patient record on success.</returns>
    /// <exception cref="UnauthorizedException">Thrown when the current user has no identity name associated with the request.</exception>
    /// <exception cref="BadRequestException">Thrown when the provided patient id is not a valid ObjectId format.</exception>
    [HttpPost("{id}/archived-test")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> ArchivePatientTest(string id, [FromBody] List<OptionList> options)
    {
        var userNameContext = HttpContext.User.Identity?.Name ??
                              throw new UnauthorizedException(HttpEnum.ErrorMessage.UnauthorizedNoPermission);

        var user = await _userService.GetUserByUserName(userNameContext);

        if (!ObjectId.TryParse(id, out var patientId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        var patient = await _patientService.FindById(patientId);
        var optionLists = patient!.Tests?.Where(c => c.Name != options.First().Name).ToList();
        options.First().Description ??= "Deleted";
        var patientToReturn =
            await _patientService.UpdatePatientMasterList(patientId, MasterListType.TestList, optionLists!, user,
                options);
        return Ok(patientToReturn);
    }

    /// <summary>
    /// Archives a patient's treatment by removing the specified treatment from the patient's treatment list and updating the master list with the archived information.
    /// </summary>
    /// <param name="id">The unique identifier of the patient whose treatment is being archived.</param>
    /// <param name="options">The list of options containing the treatment details to be archived; the first option's description defaults to "Deleted" if not provided.</param>
    /// <returns>The updated patient data after the treatment has been archived.</returns>
    /// <exception cref="UnauthorizedException">Thrown when the current user's identity name is not available in the HTTP context.</exception>
    /// <exception cref="BadRequestException">Thrown when the provided patient ID is not in a valid ObjectId format.</exception>
    [HttpPost("{id}/archived-treatment")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> ArchivePatientTreatment(string id, [FromBody] List<OptionList> options)
    {
        var userNameContext = HttpContext.User.Identity?.Name ??
                              throw new UnauthorizedException(HttpEnum.ErrorMessage.UnauthorizedNoPermission);

        var user = await _userService.GetUserByUserName(userNameContext);
        if (!ObjectId.TryParse(id, out var patientId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        var patient = await _patientService.FindById(patientId);
        //await _archivePatientProcedureService.UpdateTreatment(patient, options);
        //patient.Treatment = patient.Treatment?.Where(c=>c.Name!=options.First().Name).ToList();
        var optionLists = patient!.Treatment?.Where(c => c.Name != options.First().Name).ToList()!;
        options.First().Description ??= "Deleted";
        //await _patientService.GenerateNurseCarePlanAndInsert(MasterListType.TreatmentList, (archiveData ?? new List<OptionList>())!,patient, user);
        var patientToReturn =
            await _patientService.UpdatePatientMasterList(patientId, MasterListType.TreatmentList, optionLists!, user,
                options);
        return Ok(patientToReturn);
    }

    /// <summary>
    /// Updates an existing patient record identified by the route parameter.
    /// Validates the identifier format before delegating the update to the patient service.
    /// </summary>
    /// <param name="id">The patient identifier provided in the route, expected to be a valid ObjectId.</param>
    /// <param name="newPatient">The updated patient data sent in the request body.</param>
    /// <returns>An <see cref="IActionResult"/> containing the updated patient on success.</returns>
    /// <exception cref="BadRequestException">Thrown when the <paramref name="id"/> is not a valid ObjectId format.</exception>
    [HttpPut("{id}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdatePatient(string id, [FromBody] Patient newPatient)
    {
        if (!ObjectId.TryParse(id, out _)) throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        await _patientService.Update(newPatient);
        return Ok(newPatient);
    }

    /// <summary>
    /// Moves a patient from an existing point of care to a new one. Validates the identifiers, ensures the patient's unit allows manual moves, and performs the relocation through the patient service.
    /// </summary>
    /// <param name="patientId">The unique identifier of the patient to be relocated.</param>
    /// <param name="newPoc">The identifier of the destination point of care.</param>
    /// <param name="oldPoc">The identifier of the source point of care the patient is leaving.</param>
    /// <returns>An <see cref="IActionResult"/> containing the result of the move operation when successful.</returns>
    /// <exception cref="BadRequestException">Thrown when <paramref name="patientId"/>, <paramref name="newPoc"/>, or <paramref name="oldPoc"/> are not valid identifiers.</exception>
    /// <exception cref="NotFoundException">Thrown when no unit configuration is found for the patient's current unit.</exception>
    /// <exception cref="ConflictException">Thrown when the patient's unit is operating in automatic census mode, disallowing manual moves.</exception>
    /// <exception cref="ConflictException">Thrown when the move operation fails to complete successfully.</exception>
    [HttpGet("{patientId}/{oldPoc}/new-location/{newPoc}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> MovePatient(string patientId, string newPoc, string oldPoc)
    {
        if (!ObjectId.TryParse(patientId, out var pId) || !ObjectId.TryParse(newPoc, out var newPocId)
                                                       || !ObjectId.TryParse(oldPoc, out var oldPocId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);

        var oldPatient = await _patientService.FindById(pId);

        var unitConfig = await _unitService.FindById(oldPatient!.UnitId);
        switch (unitConfig)
        {
            case null:
                throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
            case { Configuration.ManualMove: false }:
                throw new ConflictException(HttpEnum.ErrorMessage.ServiceUnavailableCensusInAutomaticMode);
        }


        var response = await _patientService.Move(oldPatient, newPocId, oldPocId);
        //await _archivePatientProcedureService.Update(oldPatient, newPatient);
        if (response)
            return Ok(response);
        throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
    }

    /// <summary>
    /// Updates the person data associated with a patient identified by the route id. Validates the id format, ensures the patient's unit exists, and refuses the update when the unit is configured for automatic editing mode.
    /// </summary>
    /// <param name="id">The patient's identifier from the route, expected to be a valid ObjectId.</param>
    /// <param name="person">The person payload containing the data to apply to the patient.</param>
    /// <param name="patientNumber">Optional patient number override from the route; when null, the existing patient number is used, falling back to an empty string.</param>
    /// <exception cref="BadRequestException">Thrown when the supplied <paramref name="id"/> is not a valid ObjectId format.</exception>
    /// <exception cref="NotFoundException">Thrown when no unit configuration is found for the patient's unit.</exception>
    /// <exception cref="ConflictException">Thrown when the patient's unit is not configured for manual editing.</exception>
    [HttpPut("{id}/{patientNumber}/data")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdatePerson(string id, [FromBody] Person person, string? patientNumber)
    {
        if (!ObjectId.TryParse(id, out var patientId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        var patient = await _patientService.FindById(patientId);

        var unitConfig = await _unitService.FindById(patient!.UnitId);
        switch (unitConfig)
        {
            case null:
                throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
            case { Configuration.ManualEdit: false }:
                throw new ConflictException(HttpEnum.ErrorMessage.ServiceUnavailableCensusInAutomaticMode);
        }


        var number = patientNumber ?? patient.PatientNumber ?? string.Empty;
        await _patientService.UpdatePatientData(patientId, number, person);
        return Ok();
    }

    /// <summary>
    /// Updates the incoming data of an existing patient, recording the changed fields for audit purposes.
    /// Performs a per-field comparison between the current patient record and the supplied payload, populating a change-tracking object for any differences in origin, diagnosis, admission time, and auxiliary fields.
    /// </summary>
    /// <param name="id">The identifier of the patient whose incoming data will be updated; must be a valid ObjectId.</param>
    /// <param name="person">The patient payload containing the new incoming data values to apply.</param>
    /// <returns>An <see cref="IActionResult"/> representing an HTTP 200 OK response when the update succeeds.</returns>
    /// <exception cref="UnauthorizedException">Thrown when the current user identity name is not available in the HTTP context.</exception>
    /// <exception cref="BadRequestException">Thrown when the provided <paramref name="id"/> is not a valid ObjectId format.</exception>
    [HttpPut("{id}/incoming-data")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdatePatientIncomingData(string id, [FromBody] Patient person)
    {
        var userNameContext = HttpContext.User.Identity?.Name ??
                              throw new UnauthorizedException(HttpEnum.ErrorMessage.UnauthorizedNoPermission);
        if (!ObjectId.TryParse(id, out var patientId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        var user = await _userService.GetUserByUserName(userNameContext);

        var patient = await _patientService.FindById(patientId);
        var personDataChange = new PatientIncomeData();
        if ((patient!.Origin != null && !patient.Origin.Name.Equals(person.Origin?.Name)) ||
            (patient.Origin == null && person.Origin != null))
            personDataChange.Origin = person.Origin;
        if (patient.OriginAux != person.OriginAux)
            personDataChange.OriginAux = person.OriginAux;
        if (patient.DiagnosisAux != person.DiagnosisAux)
            personDataChange.DiagnosisAux = person.DiagnosisAux;
        if ((patient.Diagnosis != null && !patient.Diagnosis.Name.Equals(person.Diagnosis?.Name)) ||
            (patient.Diagnosis == null && person.Diagnosis != null))
            personDataChange.Diagnosis = person.Diagnosis;
        if (patient.AdmTime != person.AdmTime)
            personDataChange.AdmTime = person.AdmTime;

        await _patientService.UpdatePatientIncomingData(patientId, person, personDataChange, user!);

        return Ok();
    }

    /// <summary>
    /// Updates the demographic data of an existing patient, identified by the provided ID, using the supplied patient payload. Validates that the caller is authenticated and that the patient ID is a valid ObjectId before performing the update.
    /// </summary>
    /// <param name="id">The string identifier of the patient whose demographic data will be updated.</param>
    /// <param name="person">The patient object containing the updated demographic information to persist.</param>
    /// <returns>An <see cref="IActionResult"/> that returns an HTTP 200 OK response when the update succeeds.</returns>
    /// <exception cref="UnauthorizedException">Thrown when the current HTTP user identity name is not available.</exception>
    /// <exception cref="BadRequestException">Thrown when the provided <paramref name="id"/> cannot be parsed as a valid ObjectId.</exception>
    [HttpPut("{id}/demographic-data")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdatePatientDemographicData(string id, [FromBody] Patient person)
    {
        var userNameContext = HttpContext.User.Identity?.Name ??
                              throw new UnauthorizedException(HttpEnum.ErrorMessage.UnauthorizedNoPermission);
        var user = await _userService.GetUserByUserName(userNameContext);

        if (!ObjectId.TryParse(id, out var patientId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        await _patientService.FindById(patientId);
        await _patientService.UpdatePatientDemographicData(patientId, person, user);

        return Ok();
    }

    /// <summary>
    /// Updates the master list of a patient for a specified list type after validating the request, the patient identifier, and the list type, then resolving the calling user.
    /// </summary>
    /// <param name="options">The list of options to apply to the patient's master list.</param>
    /// <param name="typeName">The name of the master list type to update; must parse to a valid <see cref="MasterListType"/> value.</param>
    /// <param name="id">The patient's identifier; must be a valid ObjectId.</param>
    /// <returns>An <see cref="IActionResult"/> containing the updated patient on success.</returns>
    /// <exception cref="UnauthorizedException">Thrown when the current user has no identity name available.</exception>
    /// <exception cref="BadRequestException">Thrown when <paramref name="id"/> is not a valid ObjectId format.</exception>
    /// <exception cref="BadRequestException">Thrown when <paramref name="typeName"/> does not correspond to a valid <see cref="MasterListType"/> value.</exception>
    [HttpPost("{id}/master-list/{typeName}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdatePatientMasterList([FromBody] List<OptionList> options, string typeName,
            string id)
    {
        var userNameContext = HttpContext.User.Identity?.Name ??
                              throw new UnauthorizedException(HttpEnum.ErrorMessage.UnauthorizedNoPermission);
        if (!ObjectId.TryParse(id, out var patientId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        var user = await _userService.GetUserByUserName(userNameContext);

        if (!Enum.TryParse(typeName, out MasterListType masterListType))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestIncorrectType);

        var patient = await _patientService.UpdatePatientMasterList(patientId, masterListType, options, user, options);

        return Ok(patient);
    }

    #region ADMPanel

    /// <summary>
    /// Creates a new patient record based on the provided request, after validating that the patient number is supplied
    /// and that no existing patient with the same number is already registered.
    /// </summary>
    /// <param name="request">The administration panel request payload containing the patient details, including the patient number to be used for the new record.</param>
    /// <returns>An <see cref="IActionResult"/> containing the newly created patient on success, or a <c>Conflict</c> result if a patient with the same number already exists.</returns>
    /// <exception cref="BadRequestException">Thrown when <paramref name="request"/> does not contain a valid patient number.</exception>
    [HttpPost]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> PostNewPatient([FromBody] AdmPanelRequest request)
    {
        if (string.IsNullOrEmpty(request.PatientNumber))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        var patient = await _adminPanelService.FindPatientByPatientNumber(request.PatientNumber);
        if (patient != null) return Conflict("person number already exist");
        var newPatient = await _adminPanelService.CreatePatient(request);
        return Ok(newPatient);
    }

    /// <summary>
    /// Updates an existing patient's data in the admin panel, after verifying the patient exists.
    /// </summary>
    /// <param name="request">The admin panel request payload containing the patient information to update, supplied in the request body.</param>
    /// <returns>An <see cref="IActionResult"/> containing the result of the updated patient data wrapped in an <see cref="Microsoft.AspNetCore.Mvc.OkResult"/>.</returns>
    /// <exception cref="NotFoundException">Thrown when no patient matching the request can be found.</exception>
    [HttpPut]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdatePatientData([FromBody] AdmPanelRequest request)
    {
        var patient = await _adminPanelService.FindPatient(request);
        if (patient == null) throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
        return Ok(await _adminPanelService.UpdatePatientData(request, patient));
    }

    /// <summary>
    /// Updates the admission (entry) date of an existing patient identified by the supplied patient identifier.
    /// </summary>
    /// <param name="patient">The patient payload containing the identifier of the patient to update and the new <c>AdmTime</c> value to apply.</param>
    /// <returns>An <see cref="IActionResult"/> containing the updated patient with the new admission date when the operation succeeds.</returns>
    [HttpPut("entry-date")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdatePatientEntryDate([FromBody] Patient patient)
    {
        var newPatient = await _patientService.FindById(patient.Id);
        newPatient!.AdmTime = patient.AdmTime;
        await _patientService.Update(newPatient);
        return Ok(newPatient);
    }

    /// <summary>
    /// Updates the patient location based on the provided admin panel request payload.
    /// Requires the caller to have the AuthSome role and the appropriate permission for the "Source" resource by DisplayId.
    /// </summary>
    /// <param name="request">The admin panel request containing the data needed to update the patient location.</param>
    /// <returns>An <see cref="IActionResult"/> containing the result of the patient location update operation.</returns>
    [HttpPut("location")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public IActionResult UpdatePatientLocation([FromBody] AdmPanelRequest request)
    {
        return Ok(_adminPanelService.UpdatePatientLocation(request));
    }

    /// <summary>
    /// Updates the alternate table (altable) configuration for a patient identified by the provided id.
    /// Validates the id format as an ObjectId and requires an authenticated user with the appropriate role and permission.
    /// </summary>
    /// <param name="id">The string representation of the patient's ObjectId; must be parseable to a valid ObjectId.</param>
    /// <param name="altable">The option list containing the new altable values to apply to the patient.</param>
    /// <returns>An <see cref="IActionResult"/> containing the updated patient on success.</returns>
    /// <exception cref="UnauthorizedException">Thrown when the current user's identity name is not available in the HTTP context.</exception>
    /// <exception cref="BadRequestException">Thrown when the provided <paramref name="id"/> is not a valid ObjectId format.</exception>
    [HttpPut("{id}/altable")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> UpdatePatientAltable(string id, [FromBody] OptionList altable)
    {
        var userNameContext = HttpContext.User.Identity?.Name ??
                              throw new UnauthorizedException(HttpEnum.ErrorMessage.UnauthorizedNoPermission);
        if (!ObjectId.TryParse(id, out var patientId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        var user = await _userService.GetUserByUserName(userNameContext);
        var patient = await _patientService.UpdatePatientAltable(patientId, altable, user);

        return Ok(patient);
    }

    /// <summary>
    /// Deletes (archives) a patient by its identifier, validating the id format and verifying the patient exists before performing the soft delete operation.
    /// </summary>
    /// <param name="id">The string representation of the patient's ObjectId to be archived.</param>
    /// <returns>An <see cref="IActionResult"/> containing the archived patient wrapped in an Ok response.</returns>
    /// <exception cref="BadRequestException">Thrown when the provided <paramref name="id"/> cannot be parsed as a valid ObjectId.</exception>
    /// <exception cref="NotFoundException">Thrown when no patient is found matching the specified id.</exception>
    [HttpDelete("{id}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> DeletePatientById(string id)
    {
        if (!ObjectId.TryParse(id, out var patientId))
            throw new BadRequestException(HttpEnum.ErrorMessage.BadRequestInvalidFormat);
        var patient = await _adminPanelService.FindPatientById(patientId);
        if (patient == null) throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);

        return Ok(await _adminPanelService.ArchivePatient(patient));
    }

    #endregion
}