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

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

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

		let fromDate, toDate;
		if (params.from_date && params.to_date) {
			fromDate = moment(params.from_date).format("YYYY-MM-DD");
			toDate = moment(params.to_date).format("YYYY-MM-DD");
		} else {
			const today = moment();
			fromDate = today.clone().startOf("month").format("YYYY-MM-DD");
			toDate = moment().format("YYYY-MM-DD");
		}

		filterConditions.date = {
			[Op.between]: [fromDate, toDate],
		};

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

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

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

		let employeeCheckins = [];
		employeeCheckins = await dbSAPPS.EmployeeCheckin.findAll({
			where: filterConditions,
			order: [["modified", "DESC"]],
			include: [
				{
					model: dbSAPPS.Employee,
					as: "employee",
					attributes: ["id", "employee_name", "employee_number", "designation_id", "department_id", "shifttype_id", "company_id"],
				},
				{
					model: dbSAPPS.Company,
					as: "company",
					attributes: ["id", "display_name", "name"],
				},
				{
					model: dbSAPPS.ShiftType,
					as: "shifttype",
					attributes: [
						"id",
						"enabled",
						"name",
						"display_name",
						"code",
						"start_time",
						"end_time",
						"late_time",
						"early_time",
						"duration_break",
						"start_date_ramadhan",
						"end_date_ramadhan",
						"start_time_ramadhan",
						"end_time_ramadhan",
						"late_time_ramadhan",
						"early_time_ramadhan",
						"duration_break_ramadhan",
						"enable_auto_attendance",
						"location",
						"holidaygroup_id",
						"company_id",
					],
				},
				{
					model: dbSAPPS.HolidayListItem,
					as: "holidaylistitem",
					attributes: [
						"id",
						"display_name",
						"holiday_date",
						"description",
						"is_national_holiday",
						"is_joint_holiday",
						"is_weekend_holiday",
						"weekly_off",
						"company_id",
						"holidaylist_id",
					],
				},
				{
					model: dbSAPPS.User,
					as: "modifier", // pastikan alias sesuai relasi di model Attendance
					attributes: ["full_name"],
				},
			],
		});

		employeeCheckins = employeeCheckins.map((employeeCheckin) => {
			return {
				...employeeCheckin.dataValues,
				company_name: employeeCheckin?.company?.display_name,
				employee_name: employeeCheckin?.employee?.employee_name,
				shifttype_name: employeeCheckin?.shifttype?.display_name,
				holiday_name: employeeCheckin?.holidaylistitem?.display_name,
				modifier: employeeCheckin?.modifier?.full_name,
			};
		});

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

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

		const count = await dbSAPPS.EmployeeCheckin.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,
		};
	}
}

async function create(params) {
	const transaction = await dbSAPPS.sequelize.transaction();
	try {
		// Set default bahasa
		if (!params.language) params.language = "en";

		// Validasi wajib
		if (!params.employee_id || !params.company_id || !params.shifttype_id || !params.date || !params.time || !params.log_type) {
			await transaction.rollback();
			return {
				success: false,
				message:
					params.language === "id"
						? "Field-field berikut diperlukan: ID karyawan, ID perusahaan, ID shift, tanggal, waktu, dan tipe log."
						: "The following fields are required: employee ID, company ID, shift type ID, date, time, and log type.",
			};
		}

		// Mapping status bahasa Indonesia ke Inggris
		const statusMap = {
			Terlambat: "Late",
			"Pulang Cepat": "Left Earlier",
			"Tepat Waktu": "On Time",
			"Kerja Bagus": "Good Job",
			"Status Tidak Diketahui": "Unknown Status",
		};
		if (params.status && statusMap[params.status]) {
			params.status = statusMap[params.status];
		}

		// Cek duplikasi absensi dengan log_type sama di tanggal sama
		const existingRecord = await dbSAPPS.EmployeeCheckin.findOne({
			where: {
				employee_id: params.employee_id,
				company_id: params.company_id,
				shifttype_id: params.shifttype_id,
				date: params.date,
				log_type: params.log_type,
			},
			transaction,
		});

		if (existingRecord) {
			await transaction.rollback();
			throw new Error(
				params.language === "id"
					? `Anda sudah melakukan ${params.log_type === "IN" ? "absensi masuk" : "absensi pulang"} hari ini. Terima kasih!`
					: `You have already ${params.log_type === "IN" ? "clocked in" : "clocked out"} today. Thank you!`
			);
		}

		// Jika log_type IN, cek apakah sudah ada OUT dengan waktu lebih besar
		if (params.log_type === "IN") {
			const latestOUT = await dbSAPPS.EmployeeCheckin.findOne({
				where: {
					employee_id: params.employee_id,
					company_id: params.company_id,
					shifttype_id: params.shifttype_id,
					date: params.date,
					log_type: "OUT",
				},
				order: [["time", "DESC"]],
				transaction,
			});

			if (latestOUT && latestOUT.time < params.time) {
				await transaction.rollback();
				throw new Error(
					params.language === "id"
						? `Anda sudah melakukan absensi pulang pukul ${latestOUT.time.toLocaleString("id-ID", {
								hour: "2-digit",
								minute: "2-digit",
						  })}. Tidak bisa melakukan absensi masuk.`
						: `You have already clocked out at ${latestOUT.time.toLocaleString("en-US", {
								hour: "2-digit",
								minute: "2-digit",
						  })}. You cannot clock in again.`
				);
			}
		}

		// Buat record EmployeeCheckin baru
		await dbSAPPS.EmployeeCheckin.create(params, { transaction });

		// Tentukan need_clarification berdasarkan status dan log_type
		let needClarification = false;
		if (params.log_type === "IN" && params.status === "Late") {
			needClarification = true;
		}
		if (params.log_type === "OUT" && params.status === "Left Earlier") {
			needClarification = true;
		}

		// Cari attendance terkait
		const attendance = await dbSAPPS.Attendance.findOne({
			where: {
				employee_id: params.employee_id,
				company_id: params.company_id,
				shifttype_id: params.shifttype_id,
				date: params.date,
			},
			transaction,
		});

		if (!attendance) {
			// Buat attendance baru
			await dbSAPPS.Attendance.create(
				{
					creation: params.creation,
					modified: params.modified,
					modified_by: params.modified_by,
					owner: params.owner,

					date: params.date,
					in_time: params.log_type === "IN" ? params.time : null,
					out_time: params.log_type === "OUT" ? params.time : null,
					in_status: params.log_type === "IN" ? params.status : null,
					out_status: params.log_type === "OUT" ? params.status : null,
					in_address: params.log_type === "IN" ? params.address : null,
					out_address: params.log_type === "OUT" ? params.address : null,
					in_latitude: params.log_type === "IN" ? params.latitude : null,
					out_latitude: params.log_type === "OUT" ? params.latitude : null,
					in_longitude: params.log_type === "IN" ? params.longitude : null,
					out_longitude: params.log_type === "OUT" ? params.longitude : null,
					total_hour: 0,

					company_id: params.company_id,
					employee_id: params.employee_id,
					shifttype_id: params.shifttype_id,
					holidaylistitem_id: params.holidaylistitem_id,

					need_clarification: needClarification,
				},
				{ transaction }
			);
		} else {
			// Hitung total jam kerja jika memungkinkan
			const inTime = attendance.in_time;
			const outTime = params.log_type === "OUT" ? params.time : attendance.out_time;

			let totalHour = 0;
			if (inTime && outTime) {
				const [inHour, inMinute] = inTime.split(":").map(Number);
				const [outHour, outMinute] = outTime.split(":").map(Number);

				const inTotalMinutes = inHour * 60 + inMinute;
				const outTotalMinutes = outHour * 60 + outMinute;

				const durationBreak = params.is_ramadhan === true || params.is_ramadhan === "true" ? params?.duration_break_ramadhan || 0 : params?.duration_break || 0;

				let workedMinutes = outTotalMinutes - inTotalMinutes - durationBreak;
				if (workedMinutes < 0) workedMinutes = 0;

				totalHour = workedMinutes / 60;
			}

			// Cek status absence clarification dan permit ease
			const absenceClarification = attendance.status_absenceclarification === true || attendance.status_absenceclarification === "true";
			const permitEase = attendance.status_permitease === true || attendance.status_permitease === "true";

			if (absenceClarification || permitEase) {
				await transaction.rollback();
				throw new Error(
					params.language === "id"
						? "Anda sudah melakukan absensi dengan klarifikasi kehadiran atau izin. Silakan hubungi administrator Anda."
						: "You have already attendance by absence clarification or permit ease. Please contact your administrator."
				);
			}

			// Update attendance dengan need_clarification
			const updates = { total_hour: totalHour, need_clarification: needClarification };
			if (params.log_type === "IN") {
				updates.in_time = params.time;
				updates.in_status = params.status;
				updates.in_address = params.address;
				updates.in_latitude = params.latitude;
				updates.in_longitude = params.longitude;
			} else if (params.log_type === "OUT") {
				updates.out_time = params.time;
				updates.out_status = params.status;
				updates.out_address = params.address;
				updates.out_latitude = params.latitude;
				updates.out_longitude = params.longitude;
			}

			await attendance.update(updates, { transaction });
		}

		await transaction.commit();
		return {
			success: true,
			message: params.language === "id" ? "Berhasil disimpan!" : "Saved successfully!",
		};
	} catch (error) {
		try {
			await transaction.rollback();
		} catch (rollbackError) {
			console.error("Rollback error:", rollbackError);
		}
		return {
			success: false,
			message: error?.message || (params.language === "id" ? "Gagal menyimpan, silakan coba lagi!" : "Failed to save, please try again!"),
		};
	}
}

async function update(id, params) {
	const transaction = await dbSAPPS.sequelize.transaction();
	try {
		// Inisiasi bahasa default
		if (!params.language) params.language = "en";

		// Mapping status bahasa Indonesia ke Inggris
		if (params.status) {
			if (params.status === "Terlambat") params.status = "Late";
			else if (params.status === "Pulang Cepat") params.status = "Left Earlier";
			else if (params.status === "Tepat Waktu") params.status = "On Time";
			else if (params.status === "Status Tidak Diketahui") params.status = "Unknown Status";
			// Jika sudah dalam bahasa Inggris, biarkan
		}

		// Validasi keberadaan record EmployeeCheckin
		const employeeCheckin = await dbSAPPS.EmployeeCheckin.findByPk(id, { transaction });
		if (!employeeCheckin) {
			await transaction.rollback();
			return { success: false, message: "ID not found!" };
		}

		// Cek duplikasi absensi (kecuali record yang sedang diupdate)
		const existingRecord = await dbSAPPS.EmployeeCheckin.findOne({
			where: {
				employee_id: params.employee_id,
				company_id: params.company_id,
				shifttype_id: params.shifttype_id,
				date: params.date,
				log_type: params.log_type,
			},
			transaction,
		});

		if (existingRecord && existingRecord.id !== id) {
			throw new Error(
				params.language === "id"
					? `Anda sudah melakukan ${params.log_type === "IN" ? "absensi masuk" : "absensi pulang"} hari ini. Terima kasih!`
					: `You have already ${params.log_type === "IN" ? "clocked in" : "clocked out"} today. Thank you!`
			);
		}

		// Jika log_type IN, cek apakah sudah ada OUT dengan waktu lebih besar
		if (params.log_type === "IN") {
			const latestOUT = await dbSAPPS.EmployeeCheckin.findOne({
				where: {
					employee_id: params.employee_id,
					company_id: params.company_id,
					shifttype_id: params.shifttype_id,
					date: params.date,
					log_type: "OUT",
				},
				order: [["time", "DESC"]],
				transaction,
			});

			if (latestOUT && latestOUT.time < params.time) {
				throw new Error(
					params.language === "id"
						? `Anda sudah melakukan absensi pulang pukul ${latestOUT.time.toLocaleString("id-ID", {
								hour: "2-digit",
								minute: "2-digit",
						  })}. Tidak bisa melakukan absensi masuk.`
						: `You have already clocked out at ${latestOUT.time.toLocaleString("en-US", {
								hour: "2-digit",
								minute: "2-digit",
						  })}. You cannot clock in.`
				);
			}
		}

		// Update EmployeeCheckin
		await employeeCheckin.update(params, { transaction });

		// Cari attendance terkait
		const attendance = await dbSAPPS.Attendance.findOne({
			where: {
				employee_id: params.employee_id,
				company_id: params.company_id,
				shifttype_id: params.shifttype_id,
				date: params.date,
			},
			transaction,
		});

		// Tentukan need_clarification berdasarkan status dan log_type
		let needClarification = false;
		if (params.log_type === "IN" && params.status === "Late") {
			needClarification = true;
		}
		if (params.log_type === "OUT" && params.status === "Left Earlier") {
			needClarification = true;
		}

		if (!attendance) {
			// Buat attendance baru
			await dbSAPPS.Attendance.create(
				{
					creation: params.creation,
					modified: params.modified,
					modified_by: params.modified_by,
					owner: params.owner,

					date: params.date,
					in_time: params.log_type === "IN" ? params.time : null,
					out_time: params.log_type === "OUT" ? params.time : null,
					in_status: params.log_type === "IN" ? params.status : null,
					out_status: params.log_type === "OUT" ? params.status : null,
					in_address: params.log_type === "IN" ? params.address : null,
					out_address: params.log_type === "OUT" ? params.address : null,
					total_hour: 0,

					company_id: params.company_id,
					employee_id: params.employee_id,
					shifttype_id: params.shifttype_id,
					holidaylistitem_id: params.holidaylistitem_id,

					need_clarification: needClarification,
				},
				{ transaction }
			);
		} else {
			// Hitung total jam kerja jika memungkinkan
			const inTime = attendance.in_time;
			const outTime = params.log_type === "OUT" ? params.time : attendance.out_time;

			let totalHour = 0;
			if (inTime && outTime) {
				const [inHour, inMinute] = inTime.split(":").map(Number);
				const [outHour, outMinute] = outTime.split(":").map(Number);

				const inTotalMinutes = inHour * 60 + inMinute;
				const outTotalMinutes = outHour * 60 + outMinute;

				const durationBreak = params.is_ramadhan === true || params.is_ramadhan === "true" ? params?.duration_break_ramadhan || 0 : params?.duration_break || 0;

				let workedMinutes = outTotalMinutes - inTotalMinutes - durationBreak;
				if (workedMinutes < 0) workedMinutes = 0;

				totalHour = workedMinutes / 60;
			}

			// Update attendance dengan need_clarification
			const updates = {
				total_hour: totalHour,
				need_clarification: needClarification,
			};

			if (params.log_type === "IN") {
				updates.in_time = params.time;
				updates.in_status = params.status;
				updates.in_address = params.address;
				updates.in_latitude = params.latitude;
				updates.in_longitude = params.longitude;
			} else if (params.log_type === "OUT") {
				updates.out_time = params.time;
				updates.out_status = params.status;
				updates.out_address = params.address;
				updates.out_latitude = params.latitude;
				updates.out_longitude = params.longitude;
			}

			await attendance.update(updates, { transaction });
		}

		await transaction.commit();
		return {
			success: true,
			message: params.language === "id" ? "Berhasil disimpan!" : "Saved successfully!",
		};
	} catch (error) {
		try {
			await transaction.rollback();
		} catch (rollbackError) {
			console.error("Rollback error:", rollbackError);
		}
		return {
			success: false,
			message: error?.message || (params.language === "id" ? "Gagal menyimpan, silakan coba lagi!" : "Failed to save, please try again!"),
		};
	}
}

async function _delete(id) {
	const transaction = await dbSAPPS.sequelize.transaction();
	try {
		// Cari EmployeeCheckin dengan lock update untuk menghindari race condition
		const employeeCheckin = await dbSAPPS.EmployeeCheckin.findByPk(id, {
			transaction,
			lock: transaction.LOCK.UPDATE,
		});

		if (!employeeCheckin) {
			await transaction.rollback();
			return { success: false, message: "ID not found!" };
		}

		// Cari Attendance terkait employeeCheckin
		const attendance = await dbSAPPS.Attendance.findOne({
			where: {
				employee_id: employeeCheckin.employee_id,
				company_id: employeeCheckin.company_id,
				shifttype_id: employeeCheckin.shifttype_id,
				date: employeeCheckin.date,
			},
			transaction,
			lock: transaction.LOCK.UPDATE,
		});

		// Jika ada attendance terkait, cek dan update/destroy sesuai log_type dan kondisi
		if (attendance) {
			if (employeeCheckin.log_type === "OUT") {
				if (!attendance.in_time) {
					// Jika tidak ada in_time, hapus attendance
					await attendance.destroy({ transaction });
				} else {
					// Reset data out_time dan atribut terkait
					await attendance.update(
						{
							out_time: null,
							out_status: null,
							out_address: null,
							out_latitude: null,
							out_longitude: null,
							total_hour: null,
						},
						{ transaction }
					);
				}
			} else if (employeeCheckin.log_type === "IN") {
				if (!attendance.out_time) {
					// Jika tidak ada out_time, hapus attendance
					await attendance.destroy({ transaction });
				} else {
					// Reset data in_time dan atribut terkait
					await attendance.update(
						{
							in_time: null,
							in_status: null,
							in_address: null,
							in_latitude: null,
							in_longitude: null,
							total_hour: null,
						},
						{ transaction }
					);
				}
			}

			// Jika setelah update kedua waktu kosong, hapus attendance
			if (!attendance.in_time && !attendance.out_time) {
				await attendance.destroy({ transaction });
			}
		}

		// Hapus employeeCheckin
		await employeeCheckin.destroy({ transaction });

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