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 prescriptions 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[]} prescriptions - The array of prescriptions to format.
 * @returns {Promise<Object[]>} - The formatted array of prescriptions.
 */
const formatWithModifier = async (prescriptions) => {
	return Promise.all(
		prescriptions.map(async (prescription) => {
			const modifier = await dbSAPPS.User.findOne({
				where: {
					id: prescription.modified_by,
				},
				attributes: ["full_name"],
			});

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

/**
 * Retrieves all prescriptions with their parent prescription title.
 * @param {Object} params - The query parameters.
 * @param {number[]} params.prescriptionsId - The ids of the prescriptions to retrieve.
 * @returns {Promise<Object>} - The response object with the prescriptions 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,
			};
		}

		let prescriptions = [];
		prescriptions = await dbSAPPS.Prescription.findAll({
			where: filterConditions,
			order: [["modified", "DESC"]],
			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.PrescriptionMedicine,
					as: "prescriptionmedicines",
					attributes: [
						"id",
						"notes",
						"dosage",
						"dosage_frequency",
						"dosage_quantity",
						"dosage_interval",
						"dosage_duration",
						"available_quantity",
						"total_quantity",
						"prescription_id",
						"medicine_id",
						"modified_by",
						"modified",
						"creation",
					],
					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"],
								},
							],
						},
					],
				},
			],
		});

		prescriptions = prescriptions.map((prescription) => {
			return {
				...prescription.dataValues,
				patient_name: prescription.patient ? prescription.patient.dataValues.full_name : null,
				gender_name: prescription.patient?.gender ? prescription.patient.gender.dataValues.display_name : null,
				agerange_name: prescription.patient?.agerange ? prescription.patient.agerange.dataValues.display_name : null,
				age: prescription.patient ? prescription.patient.dataValues.age : null,
				medicines: prescription.prescriptionmedicines
					? prescription.prescriptionmedicines.map((pm) => {
							return {
								...pm.dataValues,
								source: prescription.dataValues.source,
								prescription_no: prescription.dataValues.prescription_no,
								prescription_date: prescription.dataValues.prescription_date,
								patient_id: prescription.dataValues.patient_id,
								medicine_name: pm.medicine
									? `${pm.medicine.dataValues.display_name} - ${pm.medicine.dosageform?.dataValues.display_name || ""} ${pm.medicine.strength} ${
											pm.medicine.dosageunit?.dataValues.display_name || ""
									  } - ${pm.dosage || ""}`
									: null,
							};
					  })
					: [],
			};
		});

		prescriptions = await formatWithModifier(prescriptions);

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

/**
 * Get prescription 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,
			};
		}

		let options = [];
		options = await dbSAPPS.Prescription.findAll({
			attributes: ["id", "prescription_no", "notes", "source", "prescription_date", "patient_id", "doctor_id"],
			where: filterConditions,
			order: [["id", "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.PrescriptionMedicine,
					as: "prescriptionmedicines",
					attributes: [
						"id",
						"notes",
						"dosage",
						"dosage_frequency",
						"dosage_quantity",
						"dosage_interval",
						"dosage_duration",
						"available_quantity",
						"total_quantity",
						"prescription_id",
						"medicine_id",
						"modified_by",
						"modified",
						"creation",
					],
					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"],
								},
							],
						},
					],
				},
			],
		});

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

		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 prescription items
 */

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

		const count = await dbSAPPS.Prescription.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 prescription item.
 *
 *
 * @returns {Promise<{success: boolean, message: string}>} Result of the prescription item creation process.
 */
async function create(params) {
	const transaction = await dbSAPPS.sequelize.transaction();
	try {
		// validate existing

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

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

				dosage: medicine.dosage,
				dosage_frequency: medicine.dosage_frequency,
				dosage_quantity: medicine.dosage_quantity,
				dosage_interval: medicine.dosage_interval,
				dosage_duration: medicine.dosage_duration,
				available_quantity: medicine.available_quantity,
				total_quantity: medicine.total_quantity,
				notes: medicine.notes,

				prescription_id: prescription.id,
				medicine_id: medicine.medicine_id,
			}));

			try {
				await dbSAPPS.PrescriptionMedicine.bulkCreate(prescriptionMedicines, { transaction });
			} catch (error) {
				await transaction.rollback();
				const errorMessage = error?.message || "Failed to save prescription 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 prescription item.
 *
 *
 * @returns {Promise<{success: boolean, message: string}>} Result of the prescription item update process.
 */
async function update(id, params) {
	const transaction = await dbSAPPS.sequelize.transaction();
	try {
		// validate existing
		const prescription = await dbSAPPS.Prescription.findByPk(id);
		if (!prescription) return { success: false, message: "ID not found!" };

		// validate existing

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

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

				dosage: medicine.dosage,
				dosage_frequency: medicine.dosage_frequency,
				dosage_quantity: medicine.dosage_quantity,
				dosage_interval: medicine.dosage_interval,
				dosage_duration: medicine.dosage_duration,
				available_quantity: medicine.available_quantity,
				total_quantity: medicine.total_quantity,
				notes: medicine.notes,

				prescription_id: prescription.id,
				medicine_id: medicine.medicine_id,
			}));

			try {
				await dbSAPPS.PrescriptionMedicine.destroy({
					where: { prescription_id: prescription.id },
					transaction,
				});
				await dbSAPPS.PrescriptionMedicine.bulkCreate(prescriptionMedicines, { transaction });
			} catch (error) {
				await transaction.rollback();
				const errorMessage = error?.message || "Failed to save prescription 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 prescription
 * @param {number} id - the id of the prescription
 * @returns {Promise<object>} - the result of the delete
 */
async function _delete(id) {
	const transaction = await dbSAPPS.sequelize.transaction();
	try {
		// validate existing
		const prescription = await dbSAPPS.Prescription.findByPk(id);
		if (!prescription) return { success: false, message: "ID not found!" };

		// delete
		await prescription.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,
		};
	}
}
