import path from 'path';
import envPaths from 'env-paths';
import { GatewayResponseAPI } from '../../model/studio/deploy-response-model.js';
import { constructKey } from '../../helpers/apim/test-helper.js';
import { showError, showInfo, showWarning } from '../../helpers/common/message-helper.js';
import { COMMA, CONFIG_DIRECTORY_NAME, CONFIG_FILE_NAME } from '../../constants/app-constants.js';
import { ALL_ENDPOINTS_FETCHED_FROM_STORE,
	APPEND_ENDPOINTS_FAILED, CONFIG, CONFIG_FILE_CREATED,
	CONFIG_FILE_CREATION_FAILED,
	DEPLOYMENT_DATA_UPDATED,
	FAILED_TO_SET_ENDPOINT,
	NO_DATA_TO_PROCESS,
	NO_ENDPOINTS_IN_STORE,
	NO_ENDPOINTS_STORE_CONFIGURED,
	NO_UPDATES_NEEDED,
	READ_CURRENT_ENDPOINTS_FAILED,
	SOME_ENDPOINTS_MISSING_FROM_STORE,
	UPDATE_ENDPOINTS_FAILED } from '../../constants/message-constants.js';
import { checkFileExists,
	ensureDirectoryExists,
	readJsonFromFile,
	writeJsonToFile } from '../../helpers/common/fs-helper.js';
import { DebugManager } from '../../debug/debug-manager.js';

interface Deployment {
    api: string;
    endpoints: string[];
}

export interface GatewayEndpointJson {
    deployments: Deployment[];
}

const logIfDebug = (logFn: Function, message: string) => {
    if (DebugManager.getInstance().isDebugEnabled()) {
        logFn(CONFIG + message);
    }
};

const createConfig = async (): Promise<void> => {
	const filePath = getConfigFilePath();

	try {
		await ensureDirectoryExists(path.dirname(filePath));
		if (!await checkFileExists(filePath)) {
			await writeJsonToFile(filePath, { deployments: [] }, {spaces: 2});
            logIfDebug(showInfo, CONFIG_FILE_CREATED);		}
	} catch (error) {
		logIfDebug(showError, `${CONFIG_FILE_CREATION_FAILED} ${(error as Error).message}`);
	}
};

const getConfigFilePath = (): string => {
	const paths = envPaths(CONFIG_DIRECTORY_NAME);
	return path.join(paths.config, CONFIG_FILE_NAME);
};

const formatGatewayResponse = (response: GatewayResponseAPI[]): Deployment[] => {
	return response.map(apiResponse => ({
		api: constructKey(apiResponse),
		endpoints: apiResponse.gatewayEndpoints
	}));
};

const getCurrentEndpoints = async (): Promise<GatewayEndpointJson> => {
	const filePath = getConfigFilePath();
	let currentData: GatewayEndpointJson = { deployments: [] };

	try {
		if (await checkFileExists(filePath)) {
			currentData = await readJsonFromFile(filePath) as GatewayEndpointJson;
		}
	} catch (error) {
		logIfDebug(showError, `${READ_CURRENT_ENDPOINTS_FAILED} ${(error as Error).message}`);
	}

	return currentData;
};

const updateEndpoints = async (formattedResponse: GatewayEndpointJson): Promise<void> => {
	const filePath = getConfigFilePath();

	try {
		await ensureDirectoryExists(path.dirname(filePath));
		await writeJsonToFile(filePath, formattedResponse, { spaces: 2 });
		logIfDebug(showInfo, DEPLOYMENT_DATA_UPDATED);
	} catch (error) {
		logIfDebug(showError, `${UPDATE_ENDPOINTS_FAILED} ${(error as Error).message}`);
	}
};
const compareEndpoints = (endpoints1: string[], endpoints2: string[]): boolean => {

	const sortedEndpoints1 = [...endpoints1].sort();
	const sortedEndpoints2 = [...endpoints2].sort();
	if (sortedEndpoints1.length !== sortedEndpoints2.length)
	{
		return false;
	}
	return sortedEndpoints1.every((endpoint, index) => endpoint === sortedEndpoints2[index]);
};


const overwriteEndpoints = async (formattedResponse: GatewayEndpointJson): Promise<void> => {
	try {
		const currentData = await getCurrentEndpoints();

		const updatesNeeded = formattedResponse.deployments.some(deployment => {
			const existingDeployment = currentData.deployments.find(d => d.api === deployment.api);
			return existingDeployment && !compareEndpoints(existingDeployment.endpoints, deployment.endpoints);
		});

		if (updatesNeeded) {
			await updateEndpoints(formattedResponse);
		} else {
			logIfDebug(showInfo, NO_UPDATES_NEEDED);
		}
	} catch (error) {
		logIfDebug(showError, `${UPDATE_ENDPOINTS_FAILED} ${(error as Error).message}`);
	}
};

const appendEndpoints = async (newDeployments: Deployment[]): Promise<void> => {
	const filePath = getConfigFilePath();

	try {
		const currentData = await getCurrentEndpoints();
		const deploymentsMap = new Map<string, Deployment>();

		currentData.deployments.forEach(deployment => {
			deploymentsMap.set(deployment.api, deployment);
		});

		newDeployments.forEach(deployment => {
			deploymentsMap.set(deployment.api, deployment);
		});

		currentData.deployments = Array.from(deploymentsMap.values());

		await writeJsonToFile(filePath, currentData, { spaces: 2 });
		logIfDebug(showInfo, DEPLOYMENT_DATA_UPDATED);
	} catch (error) {
		logIfDebug(showError, `${APPEND_ENDPOINTS_FAILED} ${(error as Error).message}`);
	}
};

const setGatewayEndpoint = async (response: GatewayResponseAPI[]): Promise<void> => {
	const formattedResponse: GatewayEndpointJson = { deployments: formatGatewayResponse(response) };

	if (formattedResponse.deployments.length === 0) {
		logIfDebug(showError, NO_DATA_TO_PROCESS);
		return;
	}
	const filePath = getConfigFilePath();
	try {
		if (!await checkFileExists(filePath)) {
			await createConfig();
		}
		const currentData = await getCurrentEndpoints();
		const existingApis = currentData.deployments.map(deployment => deployment.api);

		const newDeployments = formattedResponse.deployments.filter(deployment => !existingApis.includes(deployment.api));

		await overwriteEndpoints(formattedResponse);
		if (newDeployments.length > 0) {
			await appendEndpoints(newDeployments);
		}

	} catch (error) {
		logIfDebug(showError, `${FAILED_TO_SET_ENDPOINT} ${(error as Error).message}`);
	}
};


const getGatewayEndpoints = async (apiString: string): Promise<{ foundEndpoints: Record<string, string[]>; notFoundApis: string } | null> => {
	const filePath = getConfigFilePath();
    logIfDebug(showInfo, `Searching config file ${filePath} for ${apiString} endpoints`);
	if (!await checkFileExists(filePath)) {
		logIfDebug(showInfo, NO_ENDPOINTS_STORE_CONFIGURED);
		return {foundEndpoints: {}, notFoundApis: '' };
	}

	const foundEndpoints: Record<string, string[]> = {};
	const notFoundApis: string[] = [];

	const apis = apiString.split(',').map(api => api.trim());


	const data = await readJsonFromFile(filePath) as GatewayEndpointJson;

	let allApisFound = true;

	for (const api of apis) {
		showWarning(api)
		const matchedDeployment = data.deployments.find(deployment => deployment.api === api);
		if (matchedDeployment) {
			foundEndpoints[api] = matchedDeployment.endpoints;
		} else {
			notFoundApis.push(api);
			allApisFound = false;
		}
	}
	if (Object.keys(foundEndpoints).length > 0) {
		if (allApisFound) {
			logIfDebug(showInfo, ALL_ENDPOINTS_FETCHED_FROM_STORE);
		} else {
			logIfDebug(showInfo, SOME_ENDPOINTS_MISSING_FROM_STORE);
		}
	} else {
		logIfDebug(showInfo, NO_ENDPOINTS_IN_STORE);
	}

	showWarning(JSON.stringify(foundEndpoints));
	showWarning(JSON.stringify(notFoundApis));


	return { foundEndpoints, notFoundApis: notFoundApis.join(COMMA) };
};


export { getGatewayEndpoints, setGatewayEndpoint };
