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 { userId, lastUsedUserId, companyNames, update } = params;
		if (!userId || !Array.isArray(companyNames) || companyNames.length === 0) {
			return {
				success: false,
				message: "Invalid input. Please ensure that all required fields are provided in the correct format.",
			};
		}

		// validate existing
		const user = await dbSAPPS.User.findByPk(userId);
		if (!user) return { success: false, message: "ID not found!" };

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

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

		const convertToIds = await separateNameAndId(companyNames);
		const companyIds = convertToIds;

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

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

		// 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,
				  }
				: {}),
		};

		const userCompanies = await dbSAPPS.UserCompany.findAll({
			where: filterConditions,
			order: [["updatedAt", "DESC"]],
			include: [
				{
					model: dbSAPPS.User,
					as: "user",
					attributes: ["id", "email", "username", "full_name", "enabled"],
					where: userWhere,
				},
				{
					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;
		}, []);

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

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