package ejb.medicalTest;

import java.time.LocalDate;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;

import TO.MedicalTestTO;
import TO.PatientTO;
import TO.QuestionTO;
import TO.SpecialistDoctorTO;
import common.MedicalTestType;
import common.QuestionStatus;
import common.Utils;
import ejb.common.CommonFacadeLocal;
import jpa.MedicalTestJPA;
import jpa.PatientJPA;
import jpa.QuestionJPA;
import jpa.SpecialistDoctorJPA;

/**
 * EJB Session Bean Class para la Practica 2, Ejercicio 1 (ISCSD) Implementa los métodos de la capa de negocio que implementan la logica de negocio y la interacción con la capa de
 * persistencia.
 * 
 * Tanto los pacientes como los médicos deben acceder a la vista de pruebas médicas.
 * 
 * @author rorden
 *
 */
@Stateless
public class MedicalTestFacadeBean implements MedicalTestFacadeRemote {
	// Persistence Unit Context
	@PersistenceContext(unitName = "MyHealth")
	EntityManager entman;

	@EJB
	CommonFacadeLocal commonServices;

	// *********************************************************************
	// QUESTION_RESPONSE

	/**
	 * Realizar una pregunta al médico por un paciente
	 * 
	 * @param professionalNumber
	 * @param title
	 * @param message
	 */
	public String askQuestion(int userId, String title, String message) {

		PatientJPA pat = entman.find(PatientJPA.class, userId);

		if (pat.getFamilyDoctor() != null) {

			QuestionJPA qjpa = new QuestionJPA();
			qjpa.setId(0);
			qjpa.setTitle(title);
			qjpa.setMessage(message);
			qjpa.setStatus(QuestionStatus.PENDING);

			qjpa.setPatient(pat);
			qjpa.setFamilyDoctor(pat.getFamilyDoctor());

			entman.persist(qjpa);

			return "ok";

		} else {
			return "No tienes médico de familia elegido, por favor acude a la sección 'Gestionar Perfil' -> 'Cambiar médico de familia'";
		}
	}

	/**
	 * El médico responde a una pregunta
	 * 
	 * @param question
	 * @param response
	 */
	public void answerQuestion(int questionId, String response) {
		QuestionJPA qjpa;
		qjpa = entman.find(QuestionJPA.class, questionId);
		qjpa.setResponse(response);
		qjpa.setStatus(QuestionStatus.ANSWERED);

		entman.persist(qjpa);
	}

	public List<QuestionTO> listAllPendingQuestions(int familyDoctorId) {
		// TypedQuery<QuestionJPA> query = entman.createQuery("SELECT q from QuestionJPA
		// q where q.status=:status and q.familyDoctor.id=:docId order by q.title",
		// QuestionJPA.class);
		TypedQuery<QuestionJPA> query = entman.createQuery("SELECT q from QuestionJPA q where q.familyDoctor.id=:docId order by q.status desc, q.title asc", QuestionJPA.class);
		// query.setParameter("status", QuestionStatus.PENDING);
		query.setParameter("docId", familyDoctorId);

		List<QuestionJPA> allJPA = query.getResultList();
		List<QuestionTO> pendingQuestions = new ArrayList<QuestionTO>();

		for (QuestionJPA item : allJPA) {
			pendingQuestions.add(commonServices.getPOJOforQuestionJPA(item, 1));
		}

		return pendingQuestions;
	}

	public List<QuestionTO> listAllMyQuestions(int userId) {
		List<QuestionTO> pendingQuestions = new ArrayList<QuestionTO>();
		// TypedQuery<QuestionJPA> query = entman.createQuery("SELECT q from QuestionJPA
		// q where q.status=:status and q.familyDoctor.id=:docId order by q.title",
		// QuestionJPA.class);
		TypedQuery<QuestionJPA> query = entman.createQuery("SELECT q from QuestionJPA q where q.patient.id=:patientId order by q.status desc, q.title asc", QuestionJPA.class);
		// query.setParameter("status", QuestionStatus.PENDING);
		query.setParameter("patientId", userId);

		List<QuestionJPA> allJPA = query.getResultList();

		for (QuestionJPA item : allJPA) {
			pendingQuestions.add(commonServices.getPOJOforQuestionJPA(item, 1));
		}

		return pendingQuestions;
	}

	/**
	 * Recuperar una pregunta por su identificador
	 * 
	 * @param question
	 */
	public QuestionTO getQuestion(int idQuestion) {
		QuestionTO resp = new QuestionTO();

		TypedQuery<QuestionJPA> query = entman.createQuery("SELECT q from QuestionJPA q where q.id=:idquestion", QuestionJPA.class);

		query.setParameter("idquestion", idQuestion);

		resp = commonServices.getPOJOforQuestionJPA(query.getSingleResult(), 1);

		return resp;
	}

	// *********************************************************************
	// MEDICAL_TEST

	/**
	 * Añadir pruebas médicas a una cita
	 * 
	 * Dado que será añadida por el médico especialista en sesión no hace falta más información.
	 * 
	 * @param patientiID
	 * @param date
	 * @param time
	 * @param testType     Pudiera llegar a ser: Análisis de sangre, resonancias magnéticas y TAC
	 * @param observations
	 */
	public MedicalTestTO addMedicalTest(int patientID, int doctorSpecialistID, LocalDate date, LocalTime time, MedicalTestType testType, String observations) throws Exception {
		SpecialistDoctorJPA specDoctor = entman.find(SpecialistDoctorJPA.class, doctorSpecialistID);
		PatientJPA pat = entman.find(PatientJPA.class, patientID);

		MedicalTestJPA mt = new MedicalTestJPA(date, time, observations, null, testType, pat, specDoctor);

		entman.persist(mt);

		return this.commonServices.getPOJOforMedicalTestJPA(mt, 1);
	}

	/**
	 * Recuperar una prueba médica por ID
	 * 
	 * Pueden ser consultas por médicos de familia
	 * 
	 * @param idMedicalTest
	 */
	public MedicalTestTO getMedicalTest(int idMedicalTest) {
		MedicalTestTO resp = new MedicalTestTO();

		resp = commonServices.getPOJOforMedicalTestJPA(this.getMedicalTestJPA(idMedicalTest), 1);

		return resp;
	}

	private MedicalTestJPA getMedicalTestJPA(int idMedicalTest) {
		TypedQuery<MedicalTestJPA> query = entman.createQuery("SELECT q from MedicalTestJPA q where q.id=:idMedicalTest", MedicalTestJPA.class);
		query.setParameter("idMedicalTest", idMedicalTest);
		return query.getSingleResult();
	}

	/**
	 * Añadir una imagen a una prueba médica
	 * 
	 * @param idMedicalTest
	 * @param image
	 */
	public void addImage(int idMedicalTest, String image) {
		MedicalTestJPA mt = this.getMedicalTestJPA(idMedicalTest);
		mt.setHighresimage(image);

		entman.persist(mt);
	}

	/**
	 * Actualizar/Pisar una imagen a una prueba médica
	 * 
	 * @param idMedicalTest
	 * @param image
	 */
	public void updateImage(int idMedicalTest, String image) {
		this.addImage(idMedicalTest, image);
	}

	/**
	 * Eliminar una imagen a una prueba médica
	 * 
	 * @param idMedicalTest
	 */
	public void removeImage(int idMedicalTest) {
		MedicalTestJPA mt = this.getMedicalTestJPA(idMedicalTest);
		mt.setHighresimage(null);

		entman.persist(mt);
	}

	public Long getSpecialistDoctorByMedicalSpecialtyCount(int specialityId) {
		TypedQuery<Long> query = entman.createQuery("SELECT count(1) from SpecialistDoctorJPA q where q.medicalSpecialty.id=:specId", Long.class);
		query.setParameter("specId", specialityId);

		return query.getSingleResult();
	}

	/**
	 * Listar médicos con una especialidad concreta
	 * 
	 * @param speciality
	 */
	public List<SpecialistDoctorTO> findSpecialistDoctorByMedicalSpecialty(int specialityId, int pageNumber, int pageSize) {
		List<SpecialistDoctorTO> pendingQuestions = new ArrayList<SpecialistDoctorTO>();

		TypedQuery<SpecialistDoctorJPA> query = entman.createQuery(
				"SELECT q from SpecialistDoctorJPA q where q.medicalSpecialty.id=:specId order by q.medicalSpecialty.name asc, q.surname asc", SpecialistDoctorJPA.class);
		query.setParameter("specId", specialityId);

		if (pageSize > 0) {
			query.setFirstResult(pageNumber * pageSize);
			query.setMaxResults(pageSize);
		}

		List<SpecialistDoctorJPA> allJPA = query.getResultList();

		for (SpecialistDoctorJPA item : allJPA) {
			pendingQuestions.add(commonServices.getPOJOforSpecialistDoctorJPA(item, 1));
		}

		return pendingQuestions;
	}

	@Override
	public List<MedicalTestTO> loadMedicalTestForPatient(int patientID) {
		List<MedicalTestTO> medicalTests = new ArrayList<MedicalTestTO>();

		TypedQuery<MedicalTestJPA> query = entman.createQuery("SELECT q from MedicalTestJPA q where q.patient.id=:patientId order by q.id desc", MedicalTestJPA.class);

		query.setParameter("patientId", patientID);

		List<MedicalTestJPA> allJPA = query.getResultList();

		for (MedicalTestJPA item : allJPA) {
			medicalTests.add(commonServices.getPOJOforMedicalTestJPA(item, 1));
		}

		return medicalTests;
	}

	@Override
	public List<MedicalTestTO> loadMedicalTestForFamilyDoctor(int familyDoctorID) {
		return this.loadMedicalTestForFamilyDoctor(familyDoctorID, -1);
	}

	@Override
	public List<MedicalTestTO> loadMedicalTestForSpecialistDoctor(int specialistDoctorID) {
		return this.loadMedicalTestForSpecialistDoctor(specialistDoctorID, -1);
	}

	@Override
	public List<MedicalTestTO> loadMedicalTestForFamilyDoctor(int familyDoctorID, int patientID) {
		List<MedicalTestTO> medicalTests = new ArrayList<MedicalTestTO>();
		String extraQuery = "";

		if (patientID >= 0) {
			extraQuery = " and q.patient.id=:patientID";
		}

		TypedQuery<MedicalTestJPA> query = entman
				.createQuery("SELECT q from MedicalTestJPA q where q.patient.familyDoctor.id=:familyDoctorID " + extraQuery + " order by q.id desc", MedicalTestJPA.class);
		if (patientID >= 0) {
			query.setParameter("patientID", patientID);
		}
		query.setParameter("familyDoctorID", familyDoctorID);

		List<MedicalTestJPA> allJPA = query.getResultList();

		for (MedicalTestJPA item : allJPA) {
			medicalTests.add(commonServices.getPOJOforMedicalTestJPA(item, 1));
		}

		return medicalTests;
	}

	@Override
	public List<MedicalTestTO> loadMedicalTestForSpecialistDoctor(int specialistDoctorID, int patientID) {
		List<MedicalTestTO> medicalTests = new ArrayList<MedicalTestTO>();
		String extraQuery = "";

		if (patientID >= 0) {
			extraQuery = " and q.patient.id=:patientID";
		}

		TypedQuery<MedicalTestJPA> query = entman
				.createQuery("SELECT q from MedicalTestJPA q where q.specialistDoctor.id=:specialistDoctorID " + extraQuery + " order by q.id desc", MedicalTestJPA.class);

		if (patientID >= 0) {
			query.setParameter("patientID", patientID);
		}
		query.setParameter("specialistDoctorID", specialistDoctorID);

		List<MedicalTestJPA> allJPA = query.getResultList();

		for (MedicalTestJPA item : allJPA) {
			medicalTests.add(commonServices.getPOJOforMedicalTestJPA(item, 1));
		}

		return medicalTests;
	}

	public List<PatientTO> loadPatientsForSpecialistDoctor(int specialistDoctorID, String searchTerm, int pageNumber, int pageSize) {
		String strQuery = "SELECT distinct q.patient from MedicalTestJPA q where q.specialistDoctor.id=:specialistDoctorID %s order by q.patient.name, q.patient.surname";
		String strFilter = "";
		if (searchTerm == null)
			searchTerm = "";
		else
			searchTerm = Utils.normalizeTerm(searchTerm);

		if (searchTerm.length() > 0) {
			strFilter = "and (lower(q.patient.name) LIKE :searchTerm OR lower(q.patient.surname) LIKE :searchTerm)";
		}

		TypedQuery<PatientJPA> query = entman.createQuery(String.format(strQuery, strFilter), PatientJPA.class);

		if (searchTerm.length() > 0)
			query.setParameter("searchTerm", "%" + searchTerm + "%");

		query.setParameter("specialistDoctorID", specialistDoctorID);

		if (pageSize > 0) {
			query.setFirstResult(pageNumber * pageSize);
			query.setMaxResults(pageSize);
		}

		List<PatientJPA> allJPA = query.getResultList();
		List<PatientTO> patsTO = new ArrayList<PatientTO>();

		for (PatientJPA item : allJPA) {
			patsTO.add(commonServices.getPOJOforPatientJPA(item, 1));
		}

		return patsTO;
	}

	public List<PatientTO> loadPatientsForFamilyDoctor(int familyDoctorID, String searchTerm, int pageNumber, int pageSize) {
		String strQuery = "SELECT distinct q.patient from MedicalTestJPA q where q.patient.familyDoctor.id=:familyDoctorID %s order by q.patient.name, q.patient.surname";

		String strFilter = "";
		if (searchTerm == null)
			searchTerm = "";
		else
			searchTerm = Utils.normalizeTerm(searchTerm);

		if (searchTerm.length() > 0) {
			strFilter = "and (lower(q.patient.name) LIKE :searchTerm OR lower(q.patient.surname) LIKE :searchTerm)";
		}

		TypedQuery<PatientJPA> query = entman.createQuery(String.format(strQuery, strFilter), PatientJPA.class);

		if (searchTerm.length() > 0)
			query.setParameter("searchTerm", "%" + searchTerm + "%");

		query.setParameter("familyDoctorID", familyDoctorID);

		if (pageSize > 0) {
			query.setFirstResult(pageNumber * pageSize);
			query.setMaxResults(pageSize);
		}

		List<PatientJPA> allJPA = query.getResultList();
		List<PatientTO> patsTO = new ArrayList<PatientTO>();

		for (PatientJPA item : allJPA) {
			patsTO.add(commonServices.getPOJOforPatientJPA(item, 1));
		}

		return patsTO;
	}

}