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;


    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;
    }

    [HttpGet]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetAllPatients()
    {
        return Ok(await _patientService.FindAll());
    }

    [HttpGet("find-by-patientnumber/{patientNumber}")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    public async Task<IActionResult> GetByPatientNumber(string patientNumber)
    {
        return Ok(await _patientService.FindByPatientNumber(patientNumber, true));
    }

    [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);
    }

    [HttpGet("active-poc")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> GetActivePatients()
    {
        var patients = await _patientService.FindInActivePoC();
        return Ok(patients);
    }

    [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);
    }

    [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);
    }


    [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);
    }

    [HttpPost("api-request")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public async Task<IActionResult> ProcessRequestApiRequest([FromBody] ApiRequest apiRequest)
    {
        await _patientService.SaveRequestAsync(apiRequest);
        return Ok();
    }

    [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>
    ///     Salida de un paciente
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [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();
        }
    }

    [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);
    }

    [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);
    }

    [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);
    }

    [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);
    }

    [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);
    }

    [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();
    }

    [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();
    }

    [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();
    }

    [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

    [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);
    }

    [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));
    }

    [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);
    }

    [HttpPut("location")]
    [AuthorizeRoles(PermissionEnum.RolesType.AuthSome)]
    [PermissionAuthorize("Source", "DisplayId")]
    public IActionResult UpdatePatientLocation([FromBody] AdmPanelRequest request)
    {
        return Ok(_adminPanelService.UpdatePatientLocation(request));
    }

    [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);
    }

    [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
}