package managedbean.medicalTest;

import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.view.ViewScoped;
import javax.inject.Named;

import org.primefaces.event.FileUploadEvent;
import org.primefaces.event.SelectEvent;
import org.primefaces.model.UploadedFile;

import TO.MedicalTestTO;
import TO.PatientTO;
import common.Constants;
import common.MedicalTestType;
import common.UserType;
import managedbean.common.ManagedBeanBase;
import managedbean.common.SessionUtils;

@Named("mt")
@ViewScoped
public class MedicalTestMBean extends ManagedBeanBase implements Serializable {

	private static final long serialVersionUID = 1L;

	private int userID;
	private UserType userType;
	private List<MedicalTestTO> medicalTests;
	private MedicalTestTO selected;
	private PatientTO patSelected;
	private PatientTO patientFilterSelected;
	private boolean addNewMode = false;
	private UploadedFile imageUpload;
	private List<PatientTO> patientList;
	private List<PatientTO> patientWithTestList;
	private String lastUIQuery;
	private String lastUIQueryPatFilter;
	private List<MedicalTestType> medicalTestTypes;
	private LocalDate testDate;
	private LocalTime testTime;
	private String testObservations;
	private MedicalTestType testType;

	public MedicalTestMBean() {
	}

	@PostConstruct
	public void init() {
		this.userType = SessionUtils.getUserType();
		this.userID = Integer.valueOf(SessionUtils.getUserId());

		this.medicalTestTypes = new ArrayList<MedicalTestType>();
		this.medicalTestTypes.add(MedicalTestType.BLOOD_TEST);
		this.medicalTestTypes.add(MedicalTestType.CT_SCAN);
		this.medicalTestTypes.add(MedicalTestType.MAGNETIC_RESONANCE_IMAGING);

		this.selected = null;
		this.patSelected = null;
		this.lastUIQuery = "";
		this.lastUIQueryPatFilter = "";

		switch (userType) {
		case ADMINISTRATOR:
		case PATIENT:
			this.patientList = null;
			this.patientWithTestList = null;
			break;
		case SPECIALIST_DOCTOR:
			this.patientList = this.getRemoteManagerCommon().listPatientsPaged(0, Constants.MAX_ITEMS_AUTOCOMPLETE_SEARCH);
			this.patientWithTestList = this.getRemoteManagerMedicalTest().loadPatientsForSpecialistDoctor(userID, null, 0, Constants.MAX_ITEMS_AUTOCOMPLETE_SEARCH);
			break;
		case FAMILY_DOCTOR:
			this.patientList = null;
			this.patientWithTestList = this.getRemoteManagerMedicalTest().loadPatientsForFamilyDoctor(userID, null, 0, Constants.MAX_ITEMS_AUTOCOMPLETE_SEARCH);
		}

		this.loadMedicalTests();
	}

	public void uploadDataListener(FileUploadEvent event) {
	    UploadedFile uploadedFile = event.getFile();
	    String fileName = uploadedFile.getFileName();
	    String contentType = uploadedFile.getContentType();
	    byte[] contents = uploadedFile.getContents();

		if (contents != null && contents.length > 0) {
			this.selected.setHighresimage(String.format("data:%s;base64,%s", contentType, Base64.getEncoder().encodeToString(contents)));
			
			this.getRemoteManagerMedicalTest().addImage(this.selected.getId(), this.selected.getHighresimage());

			this.addFacesMessage(FacesMessage.SEVERITY_INFO, "Imagen guardada", "La imagen se ha guardado correctamente.");
		} else {
			this.addFacesMessage(FacesMessage.SEVERITY_ERROR, "Imagen vacía", "La imagen que itenta subir está vacía o no puede procesarse.");
		}
	}
	
	public UploadedFile getImageUpload() {
		return imageUpload;
	}

	public void setImageUpload(UploadedFile imageUpload) {
		this.imageUpload = imageUpload;
	}

	public void removeImage() {
		this.selected.setHighresimage(null);
		getRemoteManagerMedicalTest().removeImage(this.selected.getId());
		//this.loadMedicalTests();
		this.addFacesMessage(FacesMessage.SEVERITY_INFO, "Imagen eliminada", "La imagen se ha eliminado correctamente.");
	}

	public MedicalTestTO getSelected() {
		return this.selected;
	}

	public void setSelected(MedicalTestTO selected) {
		this.selected = selected;
	}

	public PatientTO getPatSelected() {
		return this.patSelected;
	}

	public void setPatSelected(PatientTO s) {
		this.patSelected = s;
	}

	public void loadMedicalTests() {
		Integer patId = -1;
		if (this.patientFilterSelected != null)
			patId = this.patientFilterSelected.getId();

		switch (userType) {
		case PATIENT:
			// Cargar las pruebas para el paciente en sesión
			this.medicalTests = getRemoteManagerMedicalTest().loadMedicalTestForPatient(userID);
			break;
		case SPECIALIST_DOCTOR:
			// Cargar las pruebas que el doctor especialista ha creado
			this.medicalTests = getRemoteManagerMedicalTest().loadMedicalTestForSpecialistDoctor(userID, patId);
			break;
		case FAMILY_DOCTOR:
			// Cargar las pruebas para los pacientes del doctor de familia en sesión
			this.medicalTests = getRemoteManagerMedicalTest().loadMedicalTestForFamilyDoctor(userID, patId);
			break;
		case ADMINISTRATOR:
			this.medicalTests = null;
			this.addFacesMessage(FacesMessage.SEVERITY_ERROR, "Error", "Operación no válida para el tipo de usuario actual.");
			break;
		}
	}

	public List<PatientTO> completePatient(String query) {
		if (query != null && query.equals(this.lastUIQuery) == false) {
			this.lastUIQuery = query;
			// Recuperamos las xxx primeras coincidencias
			this.patientList = this.getRemoteManagerCommon().listPatientsFiltered(query, 0, Constants.MAX_ITEMS_AUTOCOMPLETE_SEARCH);
		}
		return this.patientList;
	}

	public List<PatientTO> completePatientFilter(String query) {
		if (query != null && query.equals(this.lastUIQueryPatFilter) == false) {
			this.lastUIQueryPatFilter = query;

			switch (userType) {
			case SPECIALIST_DOCTOR:
				// Cargar los pacientes a los que ha añadido pruebas médicas el médico especialista
				this.patientWithTestList = this.getRemoteManagerMedicalTest().loadPatientsForSpecialistDoctor(userID, query, 0, Constants.MAX_ITEMS_AUTOCOMPLETE_SEARCH);
				break;
			case FAMILY_DOCTOR:
				// Cargar los pacientes del médico de familia que tiene pruebas médicas hechas
				this.patientWithTestList = this.getRemoteManagerMedicalTest().loadPatientsForFamilyDoctor(userID, query, 0, Constants.MAX_ITEMS_AUTOCOMPLETE_SEARCH);
				break;
			case ADMINISTRATOR:
			case PATIENT:
				this.addFacesMessage(FacesMessage.SEVERITY_ERROR, "Error", "Operación no válida para el tipo de usuario actual.");
				this.patientWithTestList = null;
				break;
			}
		}
		return this.patientWithTestList;
	}

	public List<PatientTO> getPatientList() {
		return patientList;
	}

	public List<PatientTO> getPatientWithTestList() {
		return patientWithTestList;
	}

	public List<MedicalTestTO> getMedicalTests() {
		return this.medicalTests;
	}

	public void setMedicalTests(List<MedicalTestTO> list) {
		// Nothing to do
	}

	public boolean isSpecialistDoctor() {
		return this.userType == UserType.SPECIALIST_DOCTOR;
	}

	public boolean isPatSelector() {
		return !(userType == UserType.PATIENT);
	}

	public void clearFilteredPatient() {
		this.addNewMode = false;
		this.selected = null;
		this.patientFilterSelected = null;
		this.loadMedicalTests();
	}

	public void onChangePatient(AjaxBehaviorEvent event) {
		this.selected = null;
		this.loadMedicalTests();
	}

	public void onSelectPatient(SelectEvent event) {
		this.selected = null;
		this.patientFilterSelected = (PatientTO) event.getObject();
		this.loadMedicalTests();
	}

	public void onSelectMT(SelectEvent event) {
		this.selected = (MedicalTestTO) event.getObject();
		this.addNewMode = false;
	}

	public void addMT() {
		// Si hay un paciente filtrado en la busqueda de pruebas, lo seleccionamos para la prueba a añadir.
		// this.patSelected = this.patientFilterSelected;
		this.testDate = LocalDate.now();
		this.testTime = LocalTime.now();
		this.testObservations = "";
		this.testType = MedicalTestType.BLOOD_TEST;
		this.addNewMode = true;
	}

	public boolean isAddNewMode() {
		return addNewMode;
	}

	public void setAddNewMode(boolean addNewMode) {
		this.addNewMode = addNewMode;
	}

	public boolean getViewCreate() {
		return addNewMode && userType == UserType.SPECIALIST_DOCTOR;
	}

	public boolean getViewEdit() {
		return !addNewMode && this.selected != null;
	}

	public List<MedicalTestType> getMedicalTestTypes() {
		return this.medicalTestTypes;
	}

	public void save() {
		try {

			MedicalTestTO mt = this.getRemoteManagerMedicalTest().addMedicalTest(this.patSelected.getId(), this.userID, this.testDate, this.testTime, this.testType,
					this.testObservations);

			this.addFacesMessage(FacesMessage.SEVERITY_INFO, "Éxito", String.format("La prueba médica se ha guardado correctamente, el identificador asignado es: %d", mt.getId()));

			// Volvemos al modo añadir (limpiamos el formulario).
			this.addMT();

			this.loadMedicalTests();
		} catch (Exception ex) {
			this.manageException(ex);
		}
	}

	public PatientTO getPatientFilterSelected() {
		return patientFilterSelected;
	}

	public void setPatientFilterSelected(PatientTO patientFilterSelected) {
		this.patientFilterSelected = patientFilterSelected;
	}

	public LocalDate getTestDate() {
		return testDate;
	}

	public void setTestDate(LocalDate testDate) {
		this.testDate = testDate;
	}

	public LocalTime getTestTime() {
		return testTime;
	}

	public void setTestTime(LocalTime testTime) {
		this.testTime = testTime;
	}

	public String getTestObservations() {
		return testObservations;
	}

	public void setTestObservations(String testObservations) {
		this.testObservations = testObservations;
	}

	public MedicalTestType getTestType() {
		return testType;
	}

	public void setTestType(MedicalTestType testType) {
		this.testType = testType;
	}

}
