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

// Choose case-insensitive operator when available (Postgres: ILIKE; others: LIKE)
const LikeOp = (() => {
	try {
		return dbSAPPS?.sequelize?.getDialect?.() === "postgres" ? Op.iLike : Op.like;
	} catch {
		return Op.like;
	}
})();

module.exports = {
	assignCompaniesToUser,
	getAllUserCompanies,
	getUnassignedCompaniesToUser,
	deleteUserCompanies,
};

async function separateNameAndId(arr) {
	if (!Array.isArray(arr)) {
		throw new Error("Input harus berupa array.");
	}
	return arr.map((str) => {
		if (typeof str !== "string") {
			throw new Error("Setiap elemen dalam array harus berupa string.");
		}
		const parts = str.split("-");
		if (parts.length < 2) {
			// Jika tidak ada "-", kembalikan nama sebagai str, id sebagai null atau kosong
			return { name: str, id: null };
		}
		const id = parts.pop(); // Ambil bagian terakhir sebagai id
		// const name = parts.join("-"); // Gabungkan sisanya sebagai nama
		return JSON.parse(id);
	});
}

/**
 * Assign an array
 */
async function assignCompaniesToUser(params) {
	const transaction = await dbSAPPS.sequelize.transaction();
	try {
		// validate input
		const { userIds, userId, lastUsedUserId, companyNames, update, append } = params;

		let targetUserIds = [];
		if (userIds && Array.isArray(userIds) && userIds.length > 0) {
			targetUserIds = userIds;
		} else if (userId) {
			targetUserIds = [userId];
		}

		if (targetUserIds.length === 0 || !Array.isArray(companyNames) || companyNames.length === 0) {
			await transaction.rollback();
			return {
				success: false,
				message: "Invalid input. Please ensure that all required fields are provided in the correct format.",
			};
		}

		const companyIds = await separateNameAndId(companyNames);

		for (const uid of targetUserIds) {
			// validate existing
			const user = await dbSAPPS.User.findByPk(uid);
			if (!user) {
				await transaction.rollback();
				return { success: false, message: "User not found!" };
			}

			const userCompanies = await dbSAPPS.UserCompany.findOne({ where: { user_id: uid } });
			if (userCompanies && update === false) {
				await transaction.rollback();
				return { success: false, message: `User ${user.email} is already assigned to a company, please update the assignment if necessary.` };
			}

			if (lastUsedUserId && update === true && !append) {
				const lastUse = await dbSAPPS.UserCompany.findOne({ where: { user_id: lastUsedUserId } });
				if (lastUse) {
					await lastUse.destroy({ transaction });
				}
			}

			// save
			if (append) {
				await user.addCompanies(companyIds, { transaction });
			} else {
				await user.setCompanies(companyIds, { transaction });
			}
		}

		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,
		};
	}
}

/**
 * Retrieves all
 */
async function getAllUserCompanies(params) {
	try {
		// filter for UserCompany (by company) and for User (search/filters)
		const filterConditions = {};
		const userFilterConditions = {};
		const andConditions = [];

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

		// column filter by user full_name
		if (params.f_full_name) {
			userFilterConditions.full_name = {
				[LikeOp]: `%${params.f_full_name}%`,
			};
		}

		// column filter by company name/display_name
		if (params.f_company_name) {
			filterConditions[Op.or] = [
				{ "$company.name$": { [LikeOp]: `%${params.f_company_name}%` } },
				{ "$company.display_name$": { [LikeOp]: `%${params.f_company_name}%` } }
			];
		}

		// global search q on user and company fields
		if (params.q) {
			andConditions.push({
				[Op.or]: [
					{ email: { [LikeOp]: `%${params.q}%` } },
					{ username: { [LikeOp]: `%${params.q}%` } },
					{ full_name: { [LikeOp]: `%${params.q}%` } },
					{ "$company.name$": { [LikeOp]: `%${params.q}%` } },
					{ "$company.display_name$": { [LikeOp]: `%${params.q}%` } }
				],
			});
		}

		const userWhere = {
			...userFilterConditions,
		};

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

		// 1. Identify UserIds that match the root filters (scope, company filter, q)
		const matchedUserCompanies = await dbSAPPS.UserCompany.findAll({
			where: rootWhere,
			attributes: ["user_id"],
			include: [
				{
					model: dbSAPPS.User,
					as: "user",
					where: userWhere,
					attributes: ["id"],
				},
				{
					model: dbSAPPS.Company,
					as: "company",
					attributes: ["id"],
				}
			],
			raw: true
		});

		const matchedUserIds = [...new Set(matchedUserCompanies.map(uc => uc.user_id))];

		if (matchedUserIds.length === 0) {
			return {
				userCompanies: [],
				success: true,
				message: "No data found!",
			};
		}

		// 2. Fetch ALL UserCompany records for these matched users to get their full company list
		const userCompanies = await dbSAPPS.UserCompany.findAll({
			where: { user_id: { [Op.in]: matchedUserIds } },
			order: [["updatedAt", "DESC"]],
			include: [
				{
					model: dbSAPPS.User,
					as: "user",
					attributes: ["id", "email", "username", "full_name", "enabled"],
				},
				{
					model: dbSAPPS.Company,
					attributes: ["id", "name", "display_name", "slug_name"],
					as: "company",
				},
			],
		});

		const result = userCompanies.reduce((acc, userCompany) => {
			const id = userCompany.user.id;
			const user_id = userCompany.user.id;
			const email = userCompany.user.email;
			const username = userCompany.user.username;
			const full_name = userCompany.user.full_name;
			const user_enabled = userCompany.user.enabled;
			const company_name = userCompany.company.name + "-" + userCompany.company.id;

			const createdAt = userCompany.createdAt;
			const updatedAt = userCompany.updatedAt;

			let existingUser = acc.find((item) => item.user_id === user_id);
			if (!existingUser) {
				existingUser = {
					id,
					user_id,
					email,
					username,
					full_name,
					user_enabled,
					companies: [],
					companiesObject: {},
					createdAt,
					updatedAt,
				};
				acc.push(existingUser);
			}

			existingUser.companies.push(company_name);
			existingUser.companiesObject = existingUser.companies.reduce((acc, name) => {
				acc[name] = true;
				return acc;
			}, {});

			return acc;
		}, []);

		let finalResult = result;
		if (params.has_single_company === true || String(params.has_single_company) === 'true') {
			finalResult = finalResult.filter(r => r.companies.length === 1);
		}

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

/**
 * Retrieve all
 */
async function getUnassignedCompaniesToUser() {
	try {
		const userIdsInUserCompanies = await dbSAPPS.UserCompany.findAll({
			attributes: ["user_id"],
			raw: true,
		}).then((result) => result.map((item) => item.user_id));

		const params = arguments[0] || {};
		const userFilterConditions = {
			id: {
				[Op.notIn]: userIdsInUserCompanies,
			},
			enabled: true,
		};
		const andConditions = [];

		// column filter by user full_name
		if (params.f_full_name) {
			userFilterConditions.full_name = {
				[LikeOp]: `%${params.f_full_name}%`,
			};
		}

		// global search q on user fields
		if (params.q) {
			andConditions.push({
				[Op.or]: [{ email: { [LikeOp]: `%${params.q}%` } }, { username: { [LikeOp]: `%${params.q}%` } }, { full_name: { [LikeOp]: `%${params.q}%` } }],
			});
		}

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

		let result;
		result = await dbSAPPS.User.findAll({
			where: userWhere,
		});

		result = result.reduce((acc, res) => {
			const id = res.id;
			const user_id = res.id;
			const email = res.email;
			const username = res.username;
			const full_name = res.full_name;
			const user_enabled = res.enabled;

			const createdAt = res.createdAt;
			const updatedAt = res.updatedAt;

			let existingUser = acc.find((item) => item.id === user_id);
			if (!existingUser) {
				existingUser = {
					id,
					user_id,
					email,
					username,
					full_name,
					user_enabled,
					companies: [],
					companiesObject: {},
					createdAt,
					updatedAt,
				};
				acc.push(existingUser);
			}

			existingUser.companies = [];
			existingUser.companiesObject = existingUser.companies.reduce((acc, name) => {
				acc[name] = true;
				return acc;
			}, {});

			return acc;
		}, []);

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

/**
 * Delete all
 */
async function deleteUserCompanies(id) {
	const transaction = await dbSAPPS.sequelize.transaction();
	try {
		const user = await dbSAPPS.User.findByPk(id);
		if (!user) return { success: false, message: "User not found!" };

		const userCompanies = await dbSAPPS.UserCompany.findAll({
			where: { user_id: id },
			transaction,
		});

		if (userCompanies.length === 0) {
			return {
				success: false,
				message: `User ${user.email} has no associated companies, so it was not deleted.`,
			};
		}

		await dbSAPPS.UserCompany.destroy({
			where: { user_id: id },
			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,
		};
	}
}
