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

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

/**
 * Get all
 */
async function getAll(params) {
	try {
		// Filters efisien: Hanya buat where jika params ada
		const filterConditions = {};

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

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

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

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

		// column filter by role name (display_name)
		if (params.f_role_name) {
			filterConditions.display_name = {
				[Op.iLike]: `%${params.f_role_name}%`,
			};
		}

		// global search q on role display_name
		const andConditions = [];
		if (params.q) {
			andConditions.push({
				[Op.or]: [{ display_name: { [Op.iLike]: `%${params.q}%` } }],
			});
		}

		const roleWhere = {
			...filterConditions,
			...(andConditions.length
				? {
						[Op.and]: andConditions,
				  }
				: {}),
		};

		// Query dengan attributes selektif untuk kecepatan
		const permissions = await dbSAPPS.Permission.findAll({
			attributes: ["id", "role_id", "doctype_id", "can_create", "can_read", "can_update", "can_delete", "scope", "createdAt", "updatedAt"], // Hanya kolom yang dibutuhkan
			include: [
				{
					model: dbSAPPS.Role,
					as: "role",
					attributes: ["id", "display_name"], // Hanya ambil yang dibutuhkan
					where: roleWhere,
				},
				{
					model: dbSAPPS.DocType,
					attributes: ["id", "name", "display_name"], // Hanya ambil yang dibutuhkan
					as: "doctype",
				},
			],
			order: [["updatedAt", "DESC"]],
		});

		// Grouping dengan reduce untuk kecepatan
		const groupedPermissions = permissions.reduce((acc, permission) => {
			const roleId = permission.role_id;
			const roleDisplayName = permission.role.display_name;
			const doctypeName = permission.doctype.name;
			const doctypeDisplayName = permission.doctype.display_name;
			const scope = permission.scope || "ALL";

			if (!acc[roleId]) {
				acc[roleId] = {
					role_id: roleId,
					role_display_name: roleDisplayName,
					role_name: roleDisplayName,
					display_name: roleDisplayName,
					doctypes: {},
					createdAt: permission.createdAt,
					updatedAt: permission.updatedAt,
				};
			}

			acc[roleId].doctypes[doctypeName] = {
				display_name: doctypeDisplayName,
				can_create: permission.can_create,
				can_read: permission.can_read,
				can_update: permission.can_update,
				can_delete: permission.can_delete,
				scope,
			};

			return acc;
		}, {});

		const result = Object.values(groupedPermissions); // Hasil sudah diurutkan dari query

		// Pengurutan eksplisit berdasarkan updatedAt untuk memastikan urutan
		result.sort((a, b) => {
			if (!a.updatedAt || !b.updatedAt) return 0; // Handle null cases
			return new Date(b.updatedAt) - new Date(a.updatedAt); // DESC: Terbaru di atas
		});

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

/**
 * Retrieves all
 */
async function getUnassignedDocTypesToRole(params) {
	try {
		const roleIdsInDocTypeRoles = await dbSAPPS.Permission.findAll({
			attributes: ["role_id"],
			raw: true,
		}).then((result) => result.map((item) => item.role_id));

		// filter
		const filterConditions = {};

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

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

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

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

		// column filter by role name
		if (params.f_role_name) {
			filterConditions.display_name = {
				[Op.iLike]: `%${params.f_role_name}%`,
			};
		}

		// global search q on role display_name
		const andConditions = [];
		if (params.q) {
			andConditions.push({
				[Op.or]: [{ display_name: { [Op.iLike]: `%${params.q}%` } }],
			});
		}

		// exclude roles that already have permissions
		filterConditions.id = {
			[Op.notIn]: roleIdsInDocTypeRoles,
		};

		// must be enabled
		filterConditions.enabled = {
			[Op.eq]: true,
		};

		const roleWhere = {
			...filterConditions,
			...(andConditions.length
				? {
						[Op.and]: andConditions,
				  }
				: {}),
		};

		let result;
		result = await dbSAPPS.Role.findAll({
			where: roleWhere,
		});

		// Grouping dengan reduce untuk kecepatan
		const groupedPermissions = result.reduce((acc, role) => {
			const roleId = role.id;
			const roleDisplayName = role.display_name;
			// const doctypeName = null;
			// const doctypeDisplayName = null;

			if (!acc[roleId]) {
				acc[roleId] = {
					role_id: roleId,
					role_display_name: roleDisplayName,
					role_name: roleDisplayName,
					display_name: roleDisplayName,
					doctypes: {},
					createdAt: role.createdAt,
					updatedAt: role.updatedAt,
				};
			}

			// acc[roleId].doctypes[doctypeName] = {
			// 	display_name: doctypeDisplayName,
			// 	can_create: null,
			// 	can_read: null,
			// 	can_update: null,
			// 	can_delete: null,
			// };

			return acc;
		}, {});

		result = Object.values(groupedPermissions); // Hasil sudah diurutkan dari query

		// Pengurutan eksplisit berdasarkan updatedAt untuk memastikan urutan
		result.sort((a, b) => {
			if (!a.updatedAt || !b.updatedAt) return 0; // Handle null cases
			return new Date(b.updatedAt) - new Date(a.updatedAt); // DESC: Terbaru di atas
		});

		return {
			permissions: result,
			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(params) {
	try {
		// filter
		const filterConditions = {};

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

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

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

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

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

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

		const count = await dbSAPPS.Permission.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
 */
async function create(params) {
	const transaction = await dbSAPPS.sequelize.transaction();
	try {
		const { role_id, doctypes } = params;
		if (!role_id) {
			return { success: false, message: "Role is required!" };
		}

		// Validasi role: Perbaiki ke model Role (asumsi ini yang dimaksud)
		const existingRole = await dbSAPPS.Permission.findOne({ where: { role_id: role_id }, attributes: ["role_id"], transaction });
		if (existingRole) {
			return { success: false, message: `Role '${role_id}' already exists, you must update!` };
		}

		if (!doctypes || Object.keys(doctypes).length === 0) {
			await transaction.commit();
			return { success: true, message: "No changes needed!" };
		}

		const promises = [];

		for (const [doctypeName, permissions] of Object.entries(doctypes)) {
			promises.push(
				(async () => {
					const doctype = await dbSAPPS.DocType.findOne({
						where: { name: doctypeName },
						attributes: ["id"],
						transaction,
					});

					if (!doctype) {
						throw new Error(`DocType with name '${doctypeName}' not found.`);
					}

					return {
						// Kembalikan data untuk bulk
						role_id,
						doctype_id: doctype.id,
						can_create: permissions.can_create || false,
						can_read: permissions.can_read !== undefined ? permissions.can_read : true,
						can_update: permissions.can_update || false,
						can_delete: permissions.can_delete || false,
						scope: permissions.scope || "ALL",
					};
				})()
			);
		}

		const bulkData = await Promise.all(promises); // Dapatkan array data

		await dbSAPPS.Permission.bulkCreate(bulkData, {
			// Gunakan bulkCreate untuk kecepatan
			transaction,
			updateOnDuplicate: ["can_create", "can_read", "can_update", "can_delete", "scope"], // Jika ada duplikat, update
		});

		// await Promise.all(promises); // Jalankan semua promises secara paralel
		await transaction.commit();
		return {
			id: role_id,
			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,
		};
	}
}

/**
 * Update an existing
 */
async function update(params) {
	const transaction = await dbSAPPS.sequelize.transaction();
	try {
		const { role_id, doctypes } = params;
		if (!role_id) {
			throw new Error("Role ID is required!");
		}
		if (!doctypes || Object.keys(doctypes).length === 0) {
			await transaction.commit(); // Commit kosong jika tidak ada data
			return { success: true, message: "No changes needed!" };
		}

		const promises = [];

		for (const [doctypeName, permissions] of Object.entries(doctypes)) {
			promises.push(
				(async () => {
					const doctype = await dbSAPPS.DocType.findOne({
						where: { name: doctypeName },
						attributes: ["id"], // Hanya ambil id untuk kecepatan
						transaction,
					});
					if (!doctype) {
						throw new Error(`DocType with name '${doctypeName}' not found.`);
					}
					return {
						// Kembalikan data untuk bulk
						role_id,
						doctype_id: doctype.id,
						can_create: permissions.can_create || false,
						can_read: permissions.can_read !== undefined ? permissions.can_read : true, // Perbaiki typo
						can_update: permissions.can_update || false,
						can_delete: permissions.can_delete || false,
						scope: permissions.scope || "ALL",
					};
				})()
			);
		}

		const bulkData = await Promise.all(promises); // Dapatkan array data

		await dbSAPPS.Permission.bulkCreate(bulkData, {
			// Gunakan bulkCreate untuk kecepatan
			transaction,
			updateOnDuplicate: ["can_create", "can_read", "can_update", "can_delete", "scope"], // Update jika ada duplikat
		});

		// await Promise.all(promises); // Jalankan semua promises secara paralel
		await transaction.commit();
		return {
			success: true,
			message: "Saved successfully!",
		};
	} catch (error) {
		await transaction.rollback();
		const errorMessage = error.message || "An error occurred during permission update.";
		return {
			success: false,
			message: errorMessage,
		};
	}
}

/**
 * Delete
 */
async function _delete(id) {
	const transaction = await dbSAPPS.sequelize.transaction();
	try {
		if (!id) {
			return res.status(400).json({
				success: false,
				message: "Role ID is required.",
			});
		}

		const deleted = await dbSAPPS.Permission.destroy({
			where: { role_id: id },
			transaction,
		});

		if (deleted === 0) {
			await transaction.rollback();
			throw new Error(`No permissions found for role_id = ${id}`);
		}

		await transaction.commit();
		return {
			success: true,
			message: "Deleted successfully!",
		};
	} catch (error) {
		await transaction.rollback();
		const errorMessage = error.message || "An error occurred during permission deletion.";
		return { success: false, message: errorMessage };
	}
}
