const dbSAPPS = require("../../helpers/db");
require("dotenv").config();
const { Op } = require("sequelize");

module.exports = {
	create,
	getAll,
	option,
	count,
	update,
	delete: _delete,
};

/**
 * Format an array of bloodPressureMonitorings by appending a modifier field to each company.
 * The modifier field is the full name of the user who last modified the company.
 * @param {Object[]} bloodPressureMonitorings - The array of bloodPressureMonitorings to format.
 * @returns {Promise<Object[]>} - The formatted array of bloodPressureMonitorings.
 */
const formatWithModifier = async (bloodPressureMonitorings) => {
	return Promise.all(
		bloodPressureMonitorings.map(async (bloodPressureMonitoring) => {
			const modifier = await dbSAPPS.User.findOne({
				where: {
					id: bloodPressureMonitoring.modified_by,
				},
				attributes: ["full_name"],
			});

			return {
				...bloodPressureMonitoring,
				modifier: modifier?.full_name,
			};
		})
	);
};

/**
 * Retrieves all bloodPressureMonitorings with their parent bloodPressureMonitoring title.
 * @param {Object} params - The query parameters.
 * @param {number[]} params.bloodPressureMonitoringsId - The ids of the bloodPressureMonitorings to retrieve.
 * @returns {Promise<Object>} - The response object with the bloodPressureMonitorings and success status.
 */
async function getAll(params) {
	try {
		// filters
		const filterConditions = {};

		if (params.patient_id) {
			filterConditions.patient_id = {
				[Op.eq]: params.patient_id,
			};
		}

		if (params.patientsId) {
			filterConditions.patient_id = {
				[Op.in]: params.patientsId,
			};
		}

		if (params.from_date && params.to_date) {
			filterConditions.date = {
				[Op.between]: [params.from_date, params.to_date],
			};
		}

		let bloodPressureMonitorings = [];
		bloodPressureMonitorings = await dbSAPPS.BloodPressureMonitoring.findAll({
			where: filterConditions,
			include: [
				{
					model: dbSAPPS.Patient,
					as: "patient",
					attributes: ["id", "full_name", "age"],
					include: [
						{
							model: dbSAPPS.Gender,
							as: "gender",
							attributes: ["id", "display_name"],
						},
						{
							model: dbSAPPS.Age,
							as: "agerange",
							attributes: ["id", "display_name"],
						},
					],
				},
				{
					model: dbSAPPS.Prescription,
					as: "prescription",
					attributes: ["id", "prescription_no", "notes", "source", "prescription_date", "patient_id", "doctor_id"],
				},
				{
					model: dbSAPPS.BloodPressureMonitoringMedicine,
					as: "bloodpressuremonitoringmedicines",
					include: [
						{
							model: dbSAPPS.Medicine,
							as: "medicine",
							attributes: ["id", "name", "display_name", "generic_name", "strength", "dosageform_id", "dosageunit_id", "manufacture_id"],
							include: [
								{
									model: dbSAPPS.DosageForm,
									as: "dosageform",
									attributes: ["id", "display_name"],
								},
								{
									model: dbSAPPS.DosageUnit,
									as: "dosageunit",
									attributes: ["id", "display_name"],
								},
							],
						},
						{
							model: dbSAPPS.PrescriptionMedicine,
							as: "prescriptionmedicine",
							attributes: ["id", "dosage"],
						},
					],
				},
			],
			order: [["modified", "DESC"]],
		});

		bloodPressureMonitorings = bloodPressureMonitorings.map((bloodPressureMonitoring) => {
			return {
				...bloodPressureMonitoring.dataValues,
				patient_name: bloodPressureMonitoring.patient ? bloodPressureMonitoring.patient.dataValues.full_name : null,
				prescription: bloodPressureMonitoring.prescription ? bloodPressureMonitoring.prescription.dataValues : null,
				gender_name: bloodPressureMonitoring.patient ? bloodPressureMonitoring.patient.gender.display_name : null,
				age: bloodPressureMonitoring.patient ? bloodPressureMonitoring.patient.age : null,
				medicines: bloodPressureMonitoring.bloodpressuremonitoringmedicines
					? bloodPressureMonitoring.bloodpressuremonitoringmedicines.map((bpm) => {
							return {
								...bpm.dataValues,
								source: bloodPressureMonitoring.prescription?.dataValues.source || null,
								prescription_no: bloodPressureMonitoring.prescription?.dataValues.prescription_no || null,
								prescription_date: bloodPressureMonitoring.prescription?.dataValues.prescription_date || null,
								patient_id: bloodPressureMonitoring?.dataValues.patient_id || null,
								medicine_name: bpm.medicine
									? `${bpm.medicine.dataValues.display_name} - ${bpm.medicine.dosageform?.dataValues.display_name || ""} ${bpm.medicine.strength} ${
											bpm.medicine.dosageunit?.dataValues.display_name || ""
									  } - ${bpm.medicine.dosage || bpm?.prescriptionmedicine?.dataValues?.dosage || ""} `
									: null,
								medicine_taken: bpm.medicine_taken,
							};
					  })
					: [],
			};
		});

		bloodPressureMonitorings = await formatWithModifier(bloodPressureMonitorings);

		const startDate = new Date(params.from_date);
		const endDate = new Date(params.to_date);
		const chartData = [];
		let currentDate = startDate;
		while (currentDate <= endDate) {
			const dateString = currentDate.toISOString().split("T")[0];
			const found = bloodPressureMonitorings.find((bpm) => bpm.date === dateString);
			const data = found
				? {
						x: dateString,
						y: [
							{
								name: "Systolic",
								value: found.systolic,
							},
							{
								name: "Diastolic",
								value: found.diastolic,
							},
							{
								name: "Pulse",
								value: found.pulse,
							},
							{
								name: "Take Medicine",
								value: found.take_medicine ? 1 : 0,
							},
						],
				  }
				: {
						x: dateString,
						y: [
							{
								name: "Systolic",
								value: 0,
							},
							{
								name: "Diastolic",
								value: 0,
							},
							{
								name: "Pulse",
								value: 0,
							},
							{
								name: "Take Medicine",
								value: 0,
							},
						],
				  };
			chartData.push(data);
			currentDate.setDate(currentDate.getDate() + 1);
		}

		const chartLineData = {
			height: 350,
			series: [
				{
					name: "Systolic",
					type: "column",
					data: chartData.map((cd) => cd.y[0].value),
				},
				{
					name: "Diastolic",
					type: "column",
					data: chartData.map((cd) => cd.y[1].value),
				},
				{
					name: "Pulse",
					type: "column",
					data: chartData.map((cd) => cd.y[2].value),
				},
				{
					name: "Take Medicine",
					type: "line",
					data: chartData.map((cd) => cd.y[3].value),
				},
			],
			options: {
				chart: {
					height: 350,
					type: "line",
					stacked: false,
					zoom: {
						enabled: false,
					},
				},
				stroke: {
					width: [0, 0, 0, 3],
					curve: "smooth",
				},

				markers: {
					size: 0,
				},
				dataLabels: {
					enabled: false,
					formatter: (val) => {
						if (val === 0) {
							return "";
						}
						return val;
					},
				},
				colors: ["#2ECC71", "#FF6384", "#ffa500", "#007BFF"],
				xaxis: {
					categories: chartData.map((cd) => cd.x),
					position: "bottom",
					axisBorder: {
						show: false,
					},
					axisTicks: {
						show: false,
					},
					crosshairs: {
						fill: {
							type: "gradient",
							gradient: {
								colorFrom: "#D8E3F0",
								colorTo: "#BED1E6",
								stops: [0, 100],
								opacityFrom: 0.4,
								opacityTo: 0.5,
							},
						},
					},
					tooltip: {
						enabled: true,
					},
				},
				yaxis: {
					axisBorder: {
						show: false,
					},
					axisTicks: {
						show: false,
					},
					labels: {
						show: false,
						formatter: function (val) {
							return val;
						},
					},
					title: {
						show: false,
					},
				},
				tooltip: {
					shared: true,
					intersect: false,
					y: {
						formatter: function (y) {
							if (typeof y !== "undefined") {
								return y.toFixed(0) + " points";
							}
							return y;
						},
					},
				},
				grid: {
					borderColor: "#e7e7e7",
					borderWidth: 1,
					xaxis: {
						lines: {
							show: false,
						},
					},
					yaxis: {
						lines: {
							show: false,
						},
					},
				},
			},
		};

		return {
			bloodPressureMonitorings,
			chartLineData,
			success: true,
			message: "Loaded successfully!",
		};
	} catch (error) {
		const errorMessage = error?.message || "Failed to load, please try again!";
		return {
			success: false,
			message: errorMessage,
		};
	}
}

/**
 * Get bloodPressureMonitoring items with their respective settings.
 */
async function option(params) {
	try {
		// filter
		const filterConditions = {};

		if (params.patient_id) {
			filterConditions.patient_id = {
				[Op.eq]: params.patient_id,
			};
		}

		if (params.patientsId) {
			filterConditions.patient_id = {
				[Op.in]: params.patientsId,
			};
		}

		if (params.from_date && params.to_date) {
			filterConditions.date = {
				[Op.between]: [params.from_date, params.to_date],
			};
		}

		let options = [];
		options = await dbSAPPS.BloodPressureMonitoring.findAll({
			attributes: ["id", "name", "display_name"],
			where: filterConditions,
			order: [["name", "ASC"]],
			include: [
				{
					model: dbSAPPS.Patient,
					as: "patient",
					attributes: ["id", "full_name", "age"],
					include: [
						{
							model: dbSAPPS.Gender,
							as: "gender",
							attributes: ["id", "display_name"],
						},
						{
							model: dbSAPPS.Age,
							as: "agerange",
							attributes: ["id", "display_name"],
						},
					],
				},
				{
					model: dbSAPPS.Prescription,
					as: "prescription",
					attributes: ["id", "prescription_no", "notes", "source", "prescription_date", "patient_id", "doctor_id"],
				},
				{
					model: dbSAPPS.BloodPressureMonitoringMedicine,
					as: "bloodpressuremonitoringmedicines",
					include: [
						{
							model: dbSAPPS.Medicine,
							as: "medicine",
							attributes: ["id", "name", "display_name", "generic_name", "strength", "dosageform_id", "dosageunit_id", "manufacture_id"],
							include: [
								{
									model: dbSAPPS.DosageForm,
									as: "dosageform",
									attributes: ["id", "display_name"],
								},
								{
									model: dbSAPPS.DosageUnit,
									as: "dosageunit",
									attributes: ["id", "display_name"],
								},
							],
						},
						{
							model: dbSAPPS.PrescriptionMedicine,
							as: "prescriptionmedicine",
							attributes: ["id", "dosage"],
						},
					],
				},
			],
		});

		options = options.map((option) => {
			return {
				...option.dataValues,
				patient_name: option.patient ? option.patient.dataValues.full_name : null,
				prescription: option.prescription ? option.prescription.dataValues : null,
				gender_name: option.patient ? option.patient.gender.display_name : null,
				age: option.patient ? option.patient.age : null,
				medicines: option.bloodpressuremonitoringmedicines
					? option.bloodpressuremonitoringmedicines.map((bpm) => {
							return {
								...bpm.dataValues,
								source: option.prescription?.dataValues.source || null,
								prescription_no: option.prescription?.dataValues.prescription_no || null,
								prescription_date: option.prescription?.dataValues.prescription_date || null,
								patient_id: option.prescription?.dataValues.patient_id || null,
								medicine_name: bpm.medicine
									? `${bpm.medicine.dataValues.display_name} - ${bpm.medicine.dosageform?.dataValues.display_name || ""} ${bpm.medicine?.strength} ${
											bpm.medicine.dosageunit?.dataValues.display_name || ""
									  } - ${bpm.medicine?.dosage || bpm?.prescriptionmedicine?.dataValues?.dosage || ""}`
									: null,
								medicine_taken: bpm.medicine_taken,
							};
					  })
					: [],
			};
		});

		options = await formatWithModifier(options);

		return {
			options,
			success: true,
			message: "Loaded successfully!",
		};
	} catch (error) {
		const errorMessage = error?.message || "Failed to load, please try again!";
		return {
			success: false,
			message: errorMessage,
		};
	}
}

/**
 * Count active bloodPressureMonitoring items
 *
 * @returns {Promise<{
 *   count: number,
 *   success: boolean,
 *   message: string
 * }>}
 */

async function count(params) {
	try {
		// filter
		const filterConditions = {};

		const count = await dbSAPPS.BloodPressureMonitoring.count({
			where: filterConditions,
		});

		return {
			count,
			success: true,
			message: "Loaded successfully!",
		};
	} catch (error) {
		const errorMessage = error?.message || "Failed to load, please try again!";
		return {
			success: false,
			message: errorMessage,
		};
	}
}

/**
 * Creates a new bloodPressureMonitoring item.
 *
 * @returns {Promise<{success: boolean, message: string}>} Result of the bloodPressureMonitoring item creation process.
 */
async function create(params) {
	const transaction = await dbSAPPS.sequelize.transaction();
	try {
		// validate existing
		const existing = await dbSAPPS.BloodPressureMonitoring.findOne({
			where: {
				patient_id: params.patient_id,
				date: params.date,
				time: params.time,
			},
			transaction,
		});
		if (existing) {
			return {
				success: false,
				message: `Patient ${params.patient_id}, date ${params.date} and time ${params.time} is already taken.`,
			};
		}

		// save
		const bloodPressureMonitoring = await dbSAPPS.BloodPressureMonitoring.create(params, { transaction });

		if (params.medicines && params.medicines.length > 0) {
			const bloodPressureMonitoringMedicines = params.medicines.map((medicine) => ({
				creation: new Date(),
				modified: new Date(),
				modified_by: params.modified_by,
				owner: params.owner,

				date: medicine.date,
				time: medicine.time,
				medicine_taken: medicine.medicine_taken,
				notes: medicine.notes,

				available_quantity: medicine.available_quantity,
				total_quantity: medicine.total_quantity,

				medicine_id: medicine.medicine_id,
				patient_id: medicine.patient_id,
				prescription_id: medicine.prescription_id,
				prescriptionmedicine_id: medicine.id,
				bloodpressuremonitoring_id: bloodPressureMonitoring.id,
			}));

			try {
				await dbSAPPS.BloodPressureMonitoringMedicine.bulkCreate(bloodPressureMonitoringMedicines, { transaction });

				// update nilai available_quantity: medicine.available_quantity, pada prescription
				const updatePrescriptionMedicine = params.medicines.map((medicine) => ({
					where: {
						id: medicine.id,
					},
					updates: {
						available_quantity: medicine.available_quantity,
					},
				}));

				for (const update of updatePrescriptionMedicine) {
					await dbSAPPS.PrescriptionMedicine.update(update.updates, {
						where: update.where,
						transaction,
					});
				}
			} catch (error) {
				await transaction.rollback();
				const errorMessage = error?.message || "Failed to save bloodPressureMonitoring medicines, please try again!";
				return {
					success: false,
					message: errorMessage,
				};
			}
		}

		await transaction.commit();
		return {
			success: true,
			message: "Saved successfully!",
		};
	} catch (error) {
		await transaction.rollback();
		const errorMessage = error?.message || "Failed to save, please try again!";
		return {
			success: false,
			message: errorMessage,
		};
	}
}

/**
 * Updates an existing bloodPressureMonitoring item.
 *
 */
async function update(id, params) {
	const transaction = await dbSAPPS.sequelize.transaction();
	try {
		// validate existing
		const bloodPressureMonitoring = await dbSAPPS.BloodPressureMonitoring.findByPk(id);
		if (!bloodPressureMonitoring) return { success: false, message: "ID not found!" };

		// validate existing
		const existing = await dbSAPPS.BloodPressureMonitoring.findOne({
			where: {
				patient_id: params.patient_id,
				date: params.date,
				time: params.time,
				id: {
					[Op.ne]: id,
				},
			},
			transaction,
		});
		if (existing) {
			return {
				success: false,
				message: `Patient ${params.patient_id}, date ${params.date} and time ${params.time} is already taken.`,
			};
		}

		// save
		await bloodPressureMonitoring.update(params, { transaction });

		if (params.medicines && params.medicines.length > 0) {
			const bloodPressureMonitoringMedicines = params.medicines.map((medicine) => ({
				creation: new Date(),
				modified: new Date(),
				modified_by: params.modified_by,
				owner: params.owner,

				date: medicine.date,
				time: medicine.time,
				medicine_taken: medicine.medicine_taken,
				notes: medicine.notes,

				available_quantity: medicine.available_quantity,
				total_quantity: medicine.total_quantity,

				medicine_id: medicine.medicine_id,
				patient_id: medicine.patient_id,
				prescription_id: medicine.prescription_id,
				prescriptionmedicine_id: medicine.id,
				bloodpressuremonitoring_id: bloodPressureMonitoring.id,
			}));

			try {
				await dbSAPPS.BloodPressureMonitoringMedicine.destroy({
					where: { bloodpressuremonitoring_id: bloodPressureMonitoring.id },
					transaction,
				});
				await dbSAPPS.BloodPressureMonitoringMedicine.bulkCreate(bloodPressureMonitoringMedicines, { transaction });

				// update nilai available_quantity: medicine.available_quantity, pada prescription
				const updatePrescriptionMedicine = params.medicines.map((medicine) => ({
					where: {
						id: medicine.id,
					},
					updates: {
						available_quantity: medicine.available_quantity,
					},
				}));

				for (const update of updatePrescriptionMedicine) {
					await dbSAPPS.PrescriptionMedicine.update(update.updates, {
						where: update.where,
						transaction,
					});
				}
			} catch (error) {
				await transaction.rollback();
				const errorMessage = error?.message || "Failed to save bloodPressureMonitoring medicines, please try again!";
				return {
					success: false,
					message: errorMessage,
				};
			}
		}

		await transaction.commit();
		return {
			success: true,
			message: "Saved successfully!",
		};
	} catch (error) {
		await transaction.rollback();
		const errorMessage = error?.message || "Failed to save, please try again!";
		return {
			success: false,
			message: errorMessage,
		};
	}
}

/**
 * Delete a bloodPressureMonitoring
 * @param {number} id - the id of the bloodPressureMonitoring
 * @returns {Promise<object>} - the result of the delete
 */
async function _delete(id) {
	const transaction = await dbSAPPS.sequelize.transaction();
	try {
		// validate existing
		const bloodPressureMonitoring = await dbSAPPS.BloodPressureMonitoring.findByPk(id);
		if (!bloodPressureMonitoring) return { success: false, message: "ID not found!" };

		// validate used in BloodPressureMonitoringMedicine
		// const usedInBloodPressureMonitoringMedicine = await dbSAPPS.BloodPressureMonitoringMedicine.count({
		// 	where: {
		// 		bloodpressuremonitoring_id: id,
		// 	},
		// 	transaction,
		// });
		// if (usedInBloodPressureMonitoringMedicine > 0) {
		// 	throw new Error(`This blood pressure monitoring is being used in medicine, cannot be deleted!`);
		// }

		// delete
		await bloodPressureMonitoring.destroy({ transaction });

		await transaction.commit();
		return {
			success: true,
			message: "Deleted successfully!",
		};
	} catch (error) {
		await transaction.rollback();
		const errorMessage = error?.message || "Failed to delete, please try again!";
		return {
			success: false,
			message: errorMessage,
		};
	}
}
