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

/** Helper to clean undefined values from an object */
const clean = (obj) => {
	const newObj = {};
	Object.keys(obj).forEach((key) => {
		if (obj[key] !== undefined) newObj[key] = obj[key];
	});
	return newObj;
};

/** Normalize field types for MySQL safety */
const normalize = (data) => {
	const booleans = ["enabled"];
	const integers = ["category_id", "location_id", "qty", "min_amt", "company_id", "owner", "modified_by"];

	booleans.forEach((f) => {
		if (data[f] !== undefined && data[f] !== null) {
			if (typeof data[f] === "string") data[f] = data[f].toLowerCase() === "true";
			else data[f] = !!data[f];
		}
	});

	integers.forEach((f) => {
		if (data[f] !== undefined && data[f] !== null) {
			if (data[f] === "" || data[f] === "null") data[f] = null;
			else {
				const val = parseInt(data[f], 10);
				data[f] = isNaN(val) ? null : val;
			}
		}
	});

	return data;
};

module.exports = {
	create,
	getAll,
	getById,
	update,
	delete: _delete,
	assignToAsset,
	removeFromAsset
};

async function logAction(params, transaction) {
	const { item_id, item_type, action, target_id, target_type, note, user_id, company_id } = params;
	await dbSAPPS.AssetLog.create({
		creation: new Date(),
		modified: new Date(),
		modified_by: user_id,
		owner: user_id,
		item_id,
		item_type,
		action,
		target_id,
		target_type,
		note,
		company_id
	}, { transaction });
}

async function getAll(params) {
	try {
		const page = parseInt(params.page, 10) || 1;
		const limit = parseInt(params.limit, 10) || 25;
		const offset = (page - 1) * limit;

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

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

		const andConditions = [];
		if (params.q && String(params.q).trim()) {
			const q = String(params.q).trim().replace(/%/g, '\\%').replace(/_/g, '\\_');
			andConditions.push({
				[Op.or]: [
					{ name: { [Op.like]: `%${q}%` } },
					{ serial: { [Op.like]: `%${q}%` } },
					{ '$company.display_name$': { [Op.like]: `%${q}%` } },
					{ '$category.display_name$': { [Op.like]: `%${q}%` } }
				]
			});
		}
		const whereClause = { ...filterConditions, ...(andConditions.length ? { [Op.and]: andConditions } : {}) };

		const { count, rows } = await dbSAPPS.Component.findAndCountAll({
			where: whereClause,
			order: [['modified', 'DESC']],
			limit,
			offset,
			subQuery: false,
			include: [
				{ model: dbSAPPS.Company, as: 'company', attributes: ['id', 'display_name'] },
				{ model: dbSAPPS.AssetCategory, as: 'category', attributes: ['id', 'display_name'] },
				{ model: dbSAPPS.AssetLocation, as: 'location', attributes: ['id', 'display_name'] },
				{ model: dbSAPPS.ComponentAsset, as: 'componentAssets' }
			]
		});

		const formatted = rows.map(r => ({
			...r.dataValues,
			company_name: r?.company?.display_name,
			category_name: r?.category?.display_name,
			location_name: r?.location?.display_name,
			qty_total: r.qty,
			qty_assigned: r.componentAssets?.length || 0,
			qty_available: r.qty - (r.componentAssets?.length || 0)
		}));

		return { components: formatted, total: count, success: true, message: 'Loaded successfully!' };
	} catch (error) {
		return { success: false, message: error?.message || 'Failed to load!' };
	}
}

async function getById(id) {
	try {
		const row = await dbSAPPS.Component.findByPk(id, {
			include: [
				{ model: dbSAPPS.Company, as: 'company' },
				{ model: dbSAPPS.AssetCategory, as: 'category' },
				{ model: dbSAPPS.AssetLocation, as: 'location' },
                { 
                    model: dbSAPPS.ComponentAsset, 
                    as: 'componentAssets',
                    include: [{ model: dbSAPPS.Asset, as: 'asset', attributes: ['asset_tag', 'name'] }]
                }
			]
		});
		if (!row) return { success: false, message: 'Component not found!' };

		const logs = await dbSAPPS.AssetLog.findAll({
			where: { item_id: id, item_type: 'component' },
			order: [['creation', 'DESC']],
			include: [
				{ model: dbSAPPS.User, as: 'executor', attributes: ['full_name'] },
				{ model: dbSAPPS.Employee, as: 'targetEmployee', attributes: ['full_name'], required: false },
				{ model: dbSAPPS.Department, as: 'targetDepartment', attributes: ['display_name'], required: false },
				{ model: dbSAPPS.Asset, as: 'targetAsset', attributes: ['asset_tag', 'name'], required: false }
			]
		});

		return { component: row, logs, success: true, message: 'Loaded successfully!' };
	} catch (error) {
		return { success: false, message: error?.message || 'Failed to load!' };
	}
}

async function create(params) {
	try {
		const data = normalize(clean(params));
		if (!data.creation) data.creation = new Date();
		data.modified = new Date();
		if (!data.owner) data.owner = 1;

		const transaction = await dbSAPPS.sequelize.transaction();
		try {
			const row = await dbSAPPS.Component.create(data, { transaction });
			await transaction.commit();
			return { success: true, message: 'Saved successfully!' };
		} catch (error) {
			if (transaction) await transaction.rollback();
			throw error;
		}
	} catch (error) {
		console.error("Component Create Error:", error);
		return { success: false, message: error?.message || 'Failed to save!' };
	}
}

async function update(id, params) {
	try {
		const row = await dbSAPPS.Component.findByPk(id);
		if (!row) return { success: false, message: 'ID not found!' };

		const updateData = normalize(clean(params));
		if (!updateData.creation) {
			if (!row.creation) updateData.creation = new Date();
			else delete updateData.creation;
		}
		updateData.modified = new Date();
		if (!updateData.modified_by) updateData.modified_by = 1;

		const transaction = await dbSAPPS.sequelize.transaction();
		try {
			await row.update(updateData, { transaction });
			
			await logAction({
				item_id: id,
				item_type: 'component',
				action: 'update',
				note: 'Component updated',
				user_id: updateData.modified_by,
				company_id: updateData.company_id || row.company_id
			}, transaction);

			await transaction.commit();
			return { success: true, message: 'Saved successfully!' };
		} catch (error) {
			if (transaction) await transaction.rollback();
			throw error;
		}
	} catch (error) {
		console.error("Component Update Error:", error);
		return { success: false, message: error?.message || 'Failed to save!' };
	}
}

async function _delete(id) {
	try {
		const row = await dbSAPPS.Component.findByPk(id);
		if (!row) return { success: false, message: 'ID not found!' };
		await row.destroy();
		return { success: true, message: 'Deleted successfully!' };
	} catch (error) {
		return { success: false, message: error?.message || 'Failed to delete!' };
	}
}

async function assignToAsset(params) {
	const transaction = await dbSAPPS.sequelize.transaction();
	try {
		const component = await dbSAPPS.Component.findByPk(params.component_id, {
            include: [{ model: dbSAPPS.ComponentAsset, as: 'componentAssets' }],
            transaction
        });
		if (!component) return { success: false, message: 'Component not found!' };
        if (component.componentAssets.length >= component.qty) return { success: false, message: 'No more components available in stock!' };

		await dbSAPPS.ComponentAsset.create(params, { transaction });
		
		await logAction({
			item_id: params.component_id,
			item_type: 'component',
			action: 'checkout',
			target_id: params.asset_id,
			target_type: 'Asset',
			note: params.note,
			user_id: params.assigned_by,
			company_id: component.company_id
		}, transaction);

		await transaction.commit();
		return { success: true, message: 'Component assigned to asset successfully!' };
	} catch (error) {
		await transaction.rollback();
		return { success: false, message: error?.message || 'Assignment failed!' };
	}
}

async function removeFromAsset(id, params) {
	const transaction = await dbSAPPS.sequelize.transaction();
	try {
		const mapping = await dbSAPPS.ComponentAsset.findByPk(id, { transaction });
		if (!mapping) return { success: false, message: 'Assignment not found!' };

        const component = await dbSAPPS.Component.findByPk(mapping.component_id, { transaction });

		await logAction({
			item_id: mapping.component_id,
			item_type: 'component',
			action: 'checkin',
			target_id: mapping.asset_id,
			target_type: 'Asset',
			note: 'Component removed from asset',
			user_id: params.user_id,
			company_id: component?.company_id
		}, transaction);

		await mapping.destroy({ transaction });
		await transaction.commit();
		return { success: true, message: 'Component removed successfully!' };
	} catch (error) {
		await transaction.rollback();
		return { success: false, message: error?.message || 'Removal failed!' };
	}
}
