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

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

/**
 * Sync data from master Territory to TerritoryVersionChild
 * This ensures that territoryversionchilds always have the latest data from master territory
 * @param {Array} territoryVersionChilds - Array of territory version child objects
 * @param {Object} transaction - Sequelize transaction object
 * @returns {Promise<Array>} - Synced territory version childs data
 */
async function syncTerritoryVersionChildsFromMaster(
	territoryVersionChilds,
	transaction
) {
	if (
		!Array.isArray(territoryVersionChilds) ||
		territoryVersionChilds.length === 0
	) {
		return territoryVersionChilds;
	}

	// Get all unique territory_ids from childs
	const territoryIds = territoryVersionChilds
		.map((child) => child.territory_id)
		.filter((id) => id != null && id !== undefined);

	if (territoryIds.length === 0) {
		return territoryVersionChilds;
	}

	// Fetch latest data from master Territory table
	const masterTerritories = await dbSAPPS.Territory.findAll({
		where: {
			id: { [Op.in]: territoryIds },
		},
		attributes: [
			"id",
			"name",
			"display_name",
			"slug_name",
			"code",
			"type",
			"is_parent",
			"description",
			"company_id",
		],
		transaction,
	});

	// Create a map for quick lookup
	const masterTerritoryMap = new Map();
	masterTerritories.forEach((territory) => {
		masterTerritoryMap.set(territory.id, territory);
	});

	// Sync each child with master data
	const syncedChilds = territoryVersionChilds.map((child) => {
		if (!child.territory_id) {
			// If no territory_id, return as is
			return child;
		}

		const masterTerritory = masterTerritoryMap.get(child.territory_id);

		if (!masterTerritory) {
			// If master territory not found, return as is (might be deleted)
			// Log warning but don't fail - allow child to keep existing data
			console.warn(
				`Master territory with id ${child.territory_id} not found. Skipping sync for this child.`
			);
			return child;
		}

		// Sync fields from master territory
		// Preserve version-specific fields: parent_id (in version context), territoryversion_id, etc.
		return {
			...child,
			// Sync these fields from master
			name: masterTerritory.name || child.name,
			display_name: masterTerritory.display_name || child.display_name,
			slug_name: masterTerritory.slug_name || child.slug_name,
			code: masterTerritory.code || child.code,
			type: masterTerritory.type || child.type,
			is_parent:
				masterTerritory.is_parent !== undefined
					? masterTerritory.is_parent
					: child.is_parent,
			description: masterTerritory.description || child.description,
			// Sync company_id from master if not explicitly set in child
			company_id: child.company_id || masterTerritory.company_id || null,
			// Preserve version-specific fields
			territory_id: child.territory_id, // Keep reference to master
			parent_id: child.parent_id, // Keep version-specific parent relationship
			territoryversion_id: child.territoryversion_id, // Keep version reference
		};
	});

	return syncedChilds;
}

async function getAll(params = {}) {
	try {
		const {
			pageIndex = 0,
			pageSize = 25,
			sortBy,
			sortDir,
			q,
			company_id,
			companiesId,
			f_name,
			f_display_name,
			f_slug_name,
			f_company,
			enabled,
		} = params || {};

		const baseWhere = {};
		if (company_id) {
			baseWhere.company_id = company_id;
		} else if (Array.isArray(companiesId) && companiesId.length > 0) {
			baseWhere.company_id = { [Op.in]: companiesId };
		}

		const andConditions = [];
		if (Object.keys(baseWhere).length > 0) {
			andConditions.push(baseWhere);
		}
		if (f_name) {
			andConditions.push({ name: { [Op.like]: `%${f_name}%` } });
		}
		if (f_display_name) {
			andConditions.push({
				display_name: { [Op.like]: `%${f_display_name}%` },
			});
		}
		if (f_slug_name) {
			andConditions.push({ slug_name: { [Op.like]: `%${f_slug_name}%` } });
		}
		if (f_company) {
			andConditions.push({ "$company.name$": { [Op.like]: `%${f_company}%` } });
		}
		if (enabled !== undefined && enabled !== null && enabled !== "") {
			const en =
				typeof enabled === "string"
					? enabled === "1" || enabled.toLowerCase() === "true"
					: !!enabled;
			andConditions.push({ enabled: { [Op.eq]: en } });
		}
		if (q && String(q).trim().length > 0) {
			const search = String(q).trim();
			andConditions.push({
				[Op.or]: [
					{ name: { [Op.like]: `%${search}%` } },
					{ display_name: { [Op.like]: `%${search}%` } },
					{ slug_name: { [Op.like]: `%${search}%` } },
					{ "$company.name$": { [Op.like]: `%${search}%` } },
				],
			});
		}

		const where = andConditions.length > 0 ? { [Op.and]: andConditions } : {};
		const safePageSize = Math.max(
			1,
			Math.min(Number.parseInt(String(pageSize), 10) || 25, 100)
		);
		const currentPageIndex = Math.max(
			0,
			Number.parseInt(String(pageIndex), 10) || 0
		);
		const offset = currentPageIndex * safePageSize;
		const sortableFields = [
			"modified",
			"display_name",
			"name",
			"valid_from",
			"valid_to",
		];
		const sortField = sortableFields.includes(sortBy) ? sortBy : "modified";
		const sortDirection =
			String(sortDir || "DESC").toUpperCase() === "ASC" ? "ASC" : "DESC";

		const { rows, count } = await dbSAPPS.TerritoryVersion.findAndCountAll({
			where,
			order: [[sortField, sortDirection]],
			limit: safePageSize,
			offset,
			include: [
				{
					model: dbSAPPS.TerritoryVersionChild,
					as: "territoryversionchilds",
					attributes: [
						"id",
						"territoryversion_id",
						"code",
						"name",
						"display_name",
						"is_parent",
						"territory_id",
						"parent_id",
						"company_id",
						"ids",
						"enabled",
						"type",
						"slug_name",
					],
					include: [
						{
							model: dbSAPPS.Territory,
							as: "parent",
							attributes: ["display_name"],
						},
					],
				},
				{
					model: dbSAPPS.Company,
					as: "company",
					attributes: ["name"],
				},
			],
		});

		const territoryVersionsWithEmployee = await Promise.all(
			rows.map(async (territoryVersion) => {
				const dataChild = await Promise.all(
					(territoryVersion.territoryversionchilds || []).map(
						async (territoryVersionChild) => {
							const empTerritory = await dbSAPPS.EmployeeTerritory.findOne({
								where: { territory_id: territoryVersionChild.territory_id },
							});
							if (!empTerritory) {
								return { ...territoryVersionChild.dataValues, employee: [] };
							}
							const employees = await dbSAPPS.Employee.findAll({
								where: { id: empTerritory.employee_id },
								attributes: ["employee_name"],
							});
							return {
								...territoryVersionChild.dataValues,
								employee: employees.map((e) => e.employee_name),
							};
						}
					)
				);
				return {
					...territoryVersion.dataValues,
					territoryversionchilds: dataChild,
				};
			})
		);

		return {
			territoryVersionsWithEmployee,
			totalCount: count,
			pageIndex: currentPageIndex,
			pageSize: safePageSize,
			sortBy: sortField,
			sortDir: sortDirection,
			success: true,
			message: "Territory versions has been loaded!",
		};
	} catch (error) {
		const errorMessage = error.message || "Failed to load territory versions!";
		return {
			success: false,
			message: errorMessage,
		};
	}
}

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

	if (params.name) {
		filterConditions.name = {
			[Op.like]: `%${params.name}%`,
		};
	}

	if (params.display_name) {
		filterConditions.display_name = {
			[Op.like]: `%${params.display_name}%`,
		};
	}

	if (params.slug_name) {
		filterConditions.slug_name = {
			[Op.like]: `%${params.slug_name}%`,
		};
	}

	if (params.enabled) {
		filterConditions.enabled = {
			[Op.eq]: params.enabled === "1" ? true : false,
		};
	}

	try {
		const territoryVersions = await dbSAPPS.TerritoryVersion.findAll({
			where: filterConditions,
			order: [["modified", "DESC"]],
			include: [
				{
					model: dbSAPPS.TerritoryVersionChild,
					as: "territoryversionchilds",
					attributes: [
						"id",
						"territoryversion_id",
						"code",
						"name",
						"display_name",
						"is_parent",
						"parent_id",
						"company_id",
						"ids",
						"enabled",
						"type",
						"slug_name",
						"territory_id",
					],
					include: [
						{
							model: dbSAPPS.Company,
							as: "company",
							attributes: ["name"],
						},
						{
							model: dbSAPPS.Territory,
							as: "parent",
							attributes: ["display_name"],
						},
					],
				},
				{
					model: dbSAPPS.Company,
					as: "company",
					attributes: ["name"],
				},
			],
		});

		return {
			territoryVersions,
			success: true,
			message: "Territory versions has been loaded!",
		};
	} catch (error) {
		console.error("Error retrieving territory versions: ", error);
		const errorMessage =
			error.message ||
			"Failed to retrieve territory versions. Please try again later.";

		return {
			success: false,
			message: errorMessage,
		};
	}
}

async function option(params) {
	try {
		const filterConditions = {};

		if (params.name) {
			filterConditions.name = {
				[Op.like]: `%${params.name}%`,
			};
		}

		if (params.display_name) {
			filterConditions.display_name = {
				[Op.like]: `%${params.display_name}%`,
			};
		}

		if (params.slug_name) {
			filterConditions.slug_name = {
				[Op.like]: `%${params.slug_name}%`,
			};
		}

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

		if (params.valid_from && params.valid_to) {
			filterConditions.valid_from = {
				[Op.lte]: params.valid_from,
			};
			filterConditions.valid_to = {
				[Op.or]: [
					{
						valid_to: {
							[Op.gte]: params.valid_to,
						},
					},
					{
						valid_to: null,
					},
				],
			};
		}

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

		const territoryVersions = await dbSAPPS.TerritoryVersion.findAll({
			attributes: [
				"id",
				"name",
				"display_name",
				"slug_name",
				"valid_from",
				"valid_to",
				"company_id",
			],
			where: filterConditions,
			order: [["modified", "DESC"]],
			include: [
				{
					model: dbSAPPS.TerritoryVersionChild,
					as: "territoryversionchilds",
					attributes: [
						"id",
						"territoryversion_id",
						"code",
						"name",
						"display_name",
						"is_parent",
						"parent_id",
						"company_id",
						"ids",
						"enabled",
					],
					include: [
						{
							model: dbSAPPS.Company,
							as: "company",
							attributes: ["name"],
						},
						{
							model: dbSAPPS.Territory,
							as: "parent",
							attributes: ["display_name"],
						},
					],
				},
				{
					model: dbSAPPS.Company,
					as: "company",
					attributes: ["name"],
				},
			],
		});

		return {
			territoryVersions,
			success: true,
			message: "Territory versions has been loaded!",
		};
	} catch (error) {
		console.error("Error retrieving territory versions: ", error);
		const errorMessage =
			error.message ||
			"Failed to retrieve territory versions. Please try again later.";

		return {
			success: false,
			message: errorMessage,
		};
	}
}

async function count() {
	try {
		const count = await dbSAPPS.TerritoryVersion.count({
			where: { enabled: true },
		});

		return {
			count,
			success: true,
			message: "Territory versions has been loaded!",
		};
	} catch (error) {
		console.error("Error counting territory versions: ", error);
		return {
			success: false,
			message:
				"Failed to retrieve count territory versions. Please try again later.",
		};
	}
}

async function create(params) {
	const transaction = await dbSAPPS.sequelize.transaction();

	try {
		// Pastikan parameter yang diperlukan ada
		if (
			!params.name ||
			!params.display_name ||
			!params.valid_from ||
			!params.valid_to ||
			!params.company_id
		) {
			return {
				success: false,
				message:
					"Name, display name, valid from, valid to & company are required!",
			};
		}

		// Periksa apakah nama sudah ada di dalam database
		const existingName = await dbSAPPS.TerritoryVersion.findOne({
			where: { name: params.name },
		});
		if (existingName) {
			return {
				success: false,
				message: `Name "${params.name}" is already taken.`,
			};
		}

		// Periksa apakah display_name sudah ada di dalam database
		const existingDisplayName = await dbSAPPS.TerritoryVersion.findOne({
			where: { display_name: params.display_name },
		});
		if (existingDisplayName) {
			return {
				success: false,
				message: `Display Name "${params.display_name}" is already taken.`,
			};
		}

		// Validasi apakah tanggal valid sudah ada dalam rentang yang sesuai
		if (params.valid_from && params.valid_to && params.company_id) {
			const existingValidDate = await dbSAPPS.TerritoryVersion.findOne({
				where: {
					company_id: params.company_id,
					valid_from: { [Op.lte]: params.valid_to },
					valid_to: { [Op.gte]: params.valid_from },
				},
			});
			if (existingValidDate) {
				return {
					success: false,
					message: `Territory version with valid from ${
						new Date(params.valid_from).toISOString().split("T")[0]
					} and valid to ${
						new Date(params.valid_to).toISOString().split("T")[0]
					} in company_id ${params.company_id} already exists.`,
				};
			}
		}

		// Jika tidak ada nilai untuk beberapa field, set default value
		if (!params.creation) params.creation = Date.now();
		if (!params.modified) params.modified = Date.now();
		if (!params.modified_by) params.modified_by = 1; // Default jika tidak ada nilai
		if (!params.owner) params.owner = 1; // Default jika tidak ada nilai

		// Buat TerritoryVersion baru
		const territoryVersion = await dbSAPPS.TerritoryVersion.create(params, {
			transaction,
		});

		// Cek dan proses jika ada territoryversionchilds
		if (params.territoryversionchilds) {
			// Pastikan `territoryversionchilds` adalah array yang valid
			if (typeof params.territoryversionchilds === "string") {
				try {
					params.territoryversionchilds = JSON.parse(
						params.territoryversionchilds
					);
				} catch (err) {
					return {
						success: false,
						message: "Invalid JSON format for territoryversionchilds",
					};
				}
			}

			// Pastikan bahwa `territoryversionchilds` adalah array yang valid dan memiliki data
			if (
				Array.isArray(params.territoryversionchilds) &&
				params.territoryversionchilds.length > 0
			) {
				// Check unique data territoryversionchilds
				const duplicates = params.territoryversionchilds.reduce(
					(acc, child, index, array) => {
						if (
							array.findIndex(
								(item) => item.display_name === child.display_name
							) !== index
						) {
							acc.push(child.display_name);
						}
						return acc;
					},
					[]
				);

				if (duplicates.length > 0) {
					await transaction.rollback();
					return {
						success: false,
						message: `Duplicate entries exist in territoryversionchilds for display names: ${duplicates.join(
							", "
						)}. Transaction rolled back.`,
					};
				}

				// Sync data from master Territory before saving
				const syncedChilds = await syncTerritoryVersionChildsFromMaster(
					params.territoryversionchilds,
					transaction
				);

				// Persiapkan data anak untuk disimpan
				const territoryVersionsChildData = syncedChilds.map(
					(territoryVersionChild) => ({
						...territoryVersionChild,
						creation: Date.now(),
						modified: Date.now(),
						modified_by: params.modified_by || 1,
						owner: params.owner || 1,
						territoryversion_id: territoryVersion.id, // Assign ID dari TerritoryVersion yang baru saja dibuat
					})
				);

				// Insert data anak menggunakan bulkCreate
				await dbSAPPS.TerritoryVersionChild.bulkCreate(
					territoryVersionsChildData,
					{ transaction }
				);
			}
		}

		// Commit transaksi jika semua berhasil
		await transaction.commit();
		return {
			success: true,
			message: "Territory version has been created!",
		};
	} catch (error) {
		// Rollback transaksi jika ada error
		await transaction.rollback();
		const errorMessage =
			error.message || "An error occurred during creating territory version.";
		return {
			success: false,
			message: errorMessage,
		};
	}
}

async function update(id, params) {
	const transaction = await dbSAPPS.sequelize.transaction();
	try {
		const territoryVersion = await dbSAPPS.TerritoryVersion.findByPk(id);
		if (!territoryVersion)
			return {
				success: false,
				message: "Territory version not found!",
			};

		if (params.name && params.name !== territoryVersion.name) {
			const existingName = await dbSAPPS.TerritoryVersion.findOne({
				where: { name: params.name },
			});
			if (existingName) {
				return {
					success: false,
					message: `Name "${params.name}" is already taken. Try again!`,
				};
			}
		}

		if (
			params.display_name &&
			params.display_name !== territoryVersion.display_name
		) {
			const existingDisplayName = await dbSAPPS.TerritoryVersion.findOne({
				where: { display_name: params.display_name },
			});
			if (existingDisplayName) {
				return {
					success: false,
					message: `Display Name "${params.display_name}" is already taken. Try again!`,
				};
			}
		}

		if (
			params.valid_from &&
			params.valid_to &&
			params.company_id &&
			id !== territoryVersion.id
		) {
			const existingValidDate = await dbSAPPS.TerritoryVersion.findOne({
				where: {
					id: { [Op.ne]: territoryVersion.id },
					company_id: params.company_id,
					valid_from: { [Op.lte]: params.valid_to },
					valid_to: { [Op.gte]: params.valid_from },
				},
			});
			if (existingValidDate)
				return {
					success: false,
					message: `Territory version with valid from ${
						new Date(params.valid_from).toISOString().split("T")[0]
					} and valid to ${
						new Date(params.valid_to).toISOString().split("T")[0]
					} in company_id ${params.company_id} already exists.`,
				};
		}

		// Update TerritoryVersion
		await territoryVersion.update(params, { transaction });

		// Pastikan `territoryversionchilds` adalah array yang valid
		if (params.territoryversionchilds) {
			// Jika formatnya string JSON, ubah menjadi array
			if (typeof params.territoryversionchilds === "string") {
				try {
					params.territoryversionchilds = JSON.parse(
						params.territoryversionchilds
					);
				} catch (err) {
					return {
						success: false,
						message: "Invalid JSON format for territoryversionchilds",
					};
				}
			}

			// Jika `territoryversionchilds` adalah array dan memiliki data
			if (
				Array.isArray(params.territoryversionchilds) &&
				params.territoryversionchilds.length > 0
			) {
				// console.log(' params.territoryversionchilds: ', params.territoryversionchilds);
				// Check unique data territoryversionchilds
				const duplicateIds = params.territoryversionchilds.reduce(
					(acc, child, index, array) => {
						const existing = array.findIndex(
							(item) => item.territory_id === child.territory_id
						);
						if (existing !== -1 && existing !== index) {
							acc.push(child.territory_id);
						}
						return acc;
					},
					[]
				);
				const duplicateDisplayNames = params.territoryversionchilds.reduce(
					(acc, child, index, array) => {
						const existing = array.findIndex(
							(item) => item.display_name === child.display_name
						);
						if (existing !== -1 && existing !== index) {
							acc.push(child.display_name);
						}
						return acc;
					},
					[]
				);

				if (duplicateIds.length > 0 || duplicateDisplayNames.length > 0) {
					await transaction.rollback();
					const errorMessage = [];
					if (duplicateIds.length > 0) {
						errorMessage.push(
							`duplicate territory id: ${duplicateIds.join(", ")}`
						);
					}
					if (duplicateDisplayNames.length > 0) {
						errorMessage.push(
							`duplicate display names: ${duplicateDisplayNames.join(", ")}`
						);
					}
					return {
						success: false,
						message: `Duplicate entries exist in territoryversionchilds. ${errorMessage.join(
							", "
						)}. Transaction rolled back.`,
					};
				}

				// Sync data from master Territory before saving
				const syncedChilds = await syncTerritoryVersionChildsFromMaster(
					params.territoryversionchilds,
					transaction
				);

				// Hapus data lama terlebih dahulu
				await dbSAPPS.TerritoryVersionChild.destroy({
					where: { territoryversion_id: id },
					transaction,
				});

				// Persiapkan data baru untuk disimpan
				const territoryVersionsChildData = syncedChilds.map(
					(territoryVersionChild) => ({
						...territoryVersionChild,
						creation: Date.now(),
						modified: Date.now(),
						modified_by: params.modified_by || 1,
						owner: params.owner || 1,
						territoryversion_id: parseInt(id),
					})
				);

				// Masukkan data anak yang baru
				try {
					await dbSAPPS.TerritoryVersionChild.bulkCreate(
						territoryVersionsChildData,
						{ transaction }
					);
				} catch (err) {
					console.log(" err: ", err);
					await transaction.rollback();
					return {
						success: false,
						message: `Error saving territoryversionchilds: ${
							err.message || "Unknown error"
						}`,
					};
				}
			} else {
				// Jika tidak ada data, hapus semua child terkait
				await dbSAPPS.TerritoryVersionChild.destroy({
					where: { territoryversion_id: id },
					transaction,
				});
			}
		}

		// Commit transaksi jika semua sukses
		await transaction.commit();
		return {
			success: true,
			message: "Territory version has been updated!",
		};
	} catch (error) {
		// Rollback jika terjadi error
		await transaction.rollback();
		const errorMessage = error.message || "Failed to update territory version.";
		return {
			success: false,
			message: errorMessage,
		};
	}
}

async function enabled(id, params) {
	try {
		const territoryVersion = await dbSAPPS.TerritoryVersion.findByPk(id);
		if (!territoryVersion)
			return {
				success: false,
				message: "Territory version not found!",
			};

		const updateData = {};
		updateData.enabled = params.enabled;
		if (params.modified_by) updateData.modified_by = params.modified_by;
		if (params.modified) updateData.modified = params.modified;

		await territoryVersion.update(updateData);

		return {
			success: true,
			message: "Territory version has been updated!",
		};
	} catch (error) {
		const errorMessage = error.message || "Failed to update territory version.";
		return {
			success: false,
			message: errorMessage,
		};
	}
}

async function _delete(id) {
	try {
		const territoryVersion = await dbSAPPS.TerritoryVersion.findByPk(id);
		if (!territoryVersion)
			return { success: false, message: "Territory version not found!" };

		await territoryVersion.destroy();
		return {
			success: true,
			message: "Territory version has been deleted!",
		};
	} catch (error) {
		const errorMessage = error.message || "Failed to delete territory version.";
		return {
			success: false,
			message: errorMessage,
		};
	}
}
