#!/usr/bin/env node

const findUp = require("find-up");
const axios = require("axios");
const chalk = require("chalk");
const dotenv = require("dotenv");
const path = require("path");
const fs = require("fs");
const compiler = require("../config/webpackSchemas.config");
const { resolveComponentsPath, resolveComponentsGriddoConfig } = require("../config/griddo-config");

dotenv.config({ path: findUp.sync(".env") });

const { API_URL, REACT_APP_API_ENDPOINT, GRIDDO_API_URL, developerKey } = process.env;
const apiURL = API_URL || REACT_APP_API_ENDPOINT || GRIDDO_API_URL;
const tempFile = "__griddo_config_parsed__.js";
const REQUIRED_API_VERSION = "11.1.1";
let failedCount = 0;
const maximumFailedAttempts = 5;

(async () => {
	console.clear();

	const instanceGriddoConfigFile = resolveComponentsGriddoConfig();
	const inputFile = instanceGriddoConfigFile;
	const outputFile = `${__dirname}/${tempFile}`;

	try {
		// Check if the API is in the correct version. If not, it waits for a minute and checks again.
		await checkIfAPIisInCorrectVersion();

		if (fs.existsSync(outputFile)) {
			fs.unlinkSync(outputFile);
		}

		if (!fs.existsSync(inputFile)) {
			throw new Error(`Griddo config file not found: ${inputFile}`);
		}

		await compiler({
			input: instanceGriddoConfigFile,
			output: { filename: tempFile, path: __dirname },
		});

		if (!fs.existsSync(outputFile)) {
			throw new Error(`Unknown error when parsing ${inputFile}`);
		}
	} catch (err) {
		console.log(`\n${chalk.red("ERROR:")} ${err.message}\n`);
		process.exit(1);
	}

	const _griddoConfig = require(path.resolve(__dirname, `./${tempFile}`));
	const griddoConfig = _griddoConfig.default;

	const griddoSchemas = griddoConfig.schemas;

	// La propiedad computed en los schemas de datos estructurados se perderá si
	// no la stringificamos primero.
	parseComputedPropInStructuredDataSchemas(griddoSchemas.contentTypes.structuredData);

	const { version } = require(resolveComponentsPath("package.json"));

	const emptySchemas = {
		config: {
			menus: [],
			themes: [],
			languages: {},
			menuItems: {},
			moduleCategories: [],
			richTextConfig: {},
		},
		ui: { components: {}, modules: {}, templates: {} },
		contentTypes: {
			dataPacks: {},
			dataPacksCategories: {},
			structuredData: {},
		},
		forms: { templates: {}, fields: {}, categories: [] },
	};

	const data = { version, ...emptySchemas, ...griddoSchemas };

	try {
		await axios({
			url: `${apiURL}/schemas`,
			headers: { authorization: developerKey },
			method: "PUT",
			data,
		});
		console.log(`${chalk.green(" ✔")} Schemas`);
	} catch (e) {
		console.log(chalk.red("ERROR:"), e.response?.data?.message || e.response || e.message);
		process.exit(1);
	}
})();

/**
 * Parse the computed property of structured data schemas to string.
 * @mutates
 */
function parseComputedPropInStructuredDataSchemas(schemas) {
	for (const schema in schemas) {
		const fields = schemas[schema].schema?.fields;

		if (!fields) continue;

		for (const field of fields) {
			const { computed = null } = field;

			if (computed) field.computed = computed.toString();
		}
	}
}

/**
 * Check if the API is in the correct version. If not, it waits for a minute and checks again.
 */

async function checkIfAPIisInCorrectVersion() {
	const actualVersion = await getCurrentAPIVersion();
	const isCorrect = checkNewVersionIsGreaterThanLast(actualVersion, REQUIRED_API_VERSION);

	if (!isCorrect) {
		console.log(failedCount);
		failedCount++;

		console.log(
			chalk.yellow(
				`API is not in the correct version. Expected version greater than "${REQUIRED_API_VERSION}" and got "${actualVersion}".\n Retry in a minute...\n`,
			),
		);

		if (failedCount >= maximumFailedAttempts) {
			console.log(chalk.red(`API is not in the correct version after ${maximumFailedAttempts} attempts. Exiting...`));
			process.exit(1);
		}

		await new Promise((resolve) => setTimeout(resolve, 60000)); // Waits for a minute
		await checkIfAPIisInCorrectVersion();
	} else {
		console.log(chalk.green("✓ API is in the correct version."));
	}
}

/**
 * Compare two versions and return true if the actual version is greater than the required version.
 */
const checkNewVersionIsGreaterThanLast = (actualVersion, requiredVersion) => {
	const [majorA, minorA, patchA] = actualVersion.split(".").map(Number);
	const [majorB, minorB, patchB] = requiredVersion.split(".").map(Number);

	return (
		majorA > majorB ||
		(majorA === majorB && minorA > minorB) ||
		(majorA === majorB && minorA === minorB && patchA >= patchB) // Asegura que 11.1.1 sea válido
	);
};

async function getCurrentAPIVersion() {
	const response = await axios.get(`${apiURL}/settings`);
	return response.data.apiVersion;
}
