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", "reassignable"];
	const integers = ["manufacturer_id", "category_id", "seats", "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,
	assignSeat,
	revokeSeat
};

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

		// Global search across key fields
		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}%` } },
					{ license_email: { [Op.like]: `%${q}%` } },
					{ serial: { [Op.like]: `%${q}%` } },
					{ '$company.display_name$': { [Op.like]: `%${q}%` } },
					{ '$manufacturer.display_name$': { [Op.like]: `%${q}%` } }
				]
			});
		}
		const whereClause = { ...filterConditions, ...(andConditions.length ? { [Op.and]: andConditions } : {}) };

		const { count, rows } = await dbSAPPS.License.findAndCountAll({
			where: whereClause,
			order: [['modified', 'DESC']],
			limit,
			offset,
			subQuery: false,
			include: [
				{ model: dbSAPPS.Company, as: 'company', attributes: ['id', 'display_name'] },
				{ model: dbSAPPS.AssetManufacturer, as: 'manufacturer', attributes: ['id', 'display_name'] },
				{ model: dbSAPPS.AssetCategory, as: 'category', attributes: ['id', 'display_name'] },
				{ model: dbSAPPS.LicenseSeat, as: 'licenseSeats', attributes: ['id', 'assigned_to', 'asset_id'] }
			]
		});

		const formatted = rows.map(r => ({
			...r.dataValues,
			company_name: r?.company?.display_name,
			manufacturer_name: r?.manufacturer?.display_name,
			category_name: r?.category?.display_name,
			seats_count: r.seats,
			seats_assigned: r.licenseSeats?.length || 0,
			seats_available: r.seats - (r.licenseSeats?.length || 0)
		}));

		return { licenses: 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.License.findByPk(id, {
			include: [
				{ model: dbSAPPS.Company, as: 'company' },
				{ model: dbSAPPS.AssetManufacturer, as: 'manufacturer' },
				{ model: dbSAPPS.AssetCategory, as: 'category' },
				{ 
					model: dbSAPPS.LicenseSeat, 
					as: 'licenseSeats',
					include: [
						{ model: dbSAPPS.Employee, as: 'employee', attributes: ['full_name'] },
						{ model: dbSAPPS.Asset, as: 'asset', attributes: ['asset_tag', 'name'] }
					]
				}
			]
		});
		if (!row) return { success: false, message: 'License not found!' };

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

		return { license: 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.License.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("License Create Error:", error);
		return { success: false, message: error?.message || 'Failed to save!' };
	}
}

async function update(id, params) {
	try {
		const row = await dbSAPPS.License.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: 'license',
				action: 'update',
				note: 'License 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("License Update Error:", error);
		return { success: false, message: error?.message || 'Failed to save!' };
	}
}

async function _delete(id) {
	try {
		const row = await dbSAPPS.License.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 assignSeat(params) {
	const transaction = await dbSAPPS.sequelize.transaction();
	try {
		const license = await dbSAPPS.License.findByPk(params.license_id, {
			include: [{ model: dbSAPPS.LicenseSeat, as: 'licenseSeats' }],
			transaction
		});
		if (!license) return { success: false, message: 'License not found!' };
		if (license.licenseSeats.length >= license.seats) return { success: false, message: 'All seats are already assigned!' };

		const seat = await dbSAPPS.LicenseSeat.create(params, { transaction });
		
		await logAction({
			item_id: params.license_id,
			item_type: 'license',
			action: 'checkout',
			target_id: params.assigned_to || params.asset_id,
			target_type: params.assigned_to ? 'Employee' : 'Asset',
			note: params.notes,
			user_id: params.assigned_by,
			company_id: license.company_id
		}, transaction);

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

async function revokeSeat(id, params) {
	const transaction = await dbSAPPS.sequelize.transaction();
	try {
		const seat = await dbSAPPS.LicenseSeat.findByPk(id, { transaction });
		if (!seat) return { success: false, message: 'Seat assignment not found!' };

		await logAction({
			item_id: seat.license_id,
			item_type: 'license',
			action: 'checkin',
			target_id: seat.assigned_to || seat.asset_id,
			target_type: seat.assigned_to ? 'Employee' : 'Asset',
			note: 'Seat revoked',
			user_id: params.user_id,
			company_id: params.company_id
		}, transaction);

		await seat.destroy({ transaction });
		await transaction.commit();
		return { success: true, message: 'Seat revoked successfully!' };
	} catch (error) {
		await transaction.rollback();
		return { success: false, message: error?.message || 'Revocation failed!' };
	}
}
