import {
	checkFileExtension,
	constructErrorResponse,
	DeployValidationException,
	isRelativePath,
	isValidAsset,
	SpecObject,
	YamlContent,
	ZipProcessor,
} from '../index.js';
import { BuildProjectAssets } from '../build-project-assets.js';
import yaml from 'js-yaml';
import path from 'path';
import JSZip from 'jszip';
import { LogWrapper } from '../service/log-wrapper.js';
import { AppConstants } from '../constants/app.constants.js';
export const processBuild = async (fileBuffer: Buffer) => {
	LogWrapper.logInfo('0003', 'Starting build process.');

	const obj = new ZipProcessor(fileBuffer);
	LogWrapper.logInfo('0003', 'Processing zip file.');
	const result = await obj.processZip();

	if (!result) {
		LogWrapper.logError('0003', 'Build process failed.');
		throw new Error(JSON.stringify(constructErrorResponse()));
	}

	LogWrapper.logInfo('0003', 'Build process succeeded.');
	const { buildZip: gatewayZip } = result;
	LogWrapper.logInfo('0003', 'Generating zip buffer from gatewayZip.');
	const zipBuffer = await gatewayZip.generateAsync({ type: 'nodebuffer' });
	LogWrapper.logInfo('0003', 'Zip buffer generation successful.');
	return { zipBuffer };
};

export const processGatewayJson = async (fileBuffer: Buffer) => {
	LogWrapper.logInfo('0003', 'Starting extraction of gateway JSON.');

	const obj = new ZipProcessor(fileBuffer);
	return obj.extractGatewaysJson(fileBuffer);
};

export const normalizeZipPaths = async (zip: JSZip) => {
	LogWrapper.logDebug('0003', 'Starting path normalization in zip file.');

	const normalizedZip = new JSZip();
	await Promise.all(
		Object.values(zip.files).map(async (file) => {
			const normalizedPath = path.normalize(file.name);
			if (file.dir) {
				normalizedZip.folder(normalizedPath);
				LogWrapper.logDebug('0301', 'directory', `${normalizedPath}`);
			} else {
				const fileData = await file.async('nodebuffer');
				normalizedZip.file(normalizedPath, fileData);
				LogWrapper.logDebug('0301', 'file', `${normalizedPath}`);
			}
		})
	);

	LogWrapper.logDebug('0003', 'Path normalization completed.');
	return normalizedZip;
};
export const updateRelativePath = async (
	asset: YamlContent,
	basePath: string
): Promise<YamlContent> => {
	const extract = (obj: SpecObject) => {
		for (const key in obj) {
			const value = obj[key];
			if (key === AppConstants.pathVariable && typeof value === 'string') {
				if(isRelativePath(value))
				{
					
					const baseDir = path.dirname(basePath);
					const resolvedPath = path.join(baseDir, value);
					const normalizedPath = path.normalize(resolvedPath).replace(/\\+/g, '/');
					obj[key] = normalizedPath.slice(normalizedPath.indexOf('/')+1);
				}
			} 
			else if (typeof value === 'object' && value !== null) 
			{
				extract(value);
			}
		}
	};

	if (asset.spec)
	{
		extract(asset.spec as unknown as SpecObject);
	}

	return asset;
};

export const updatePathValueInContent = async (
	content: string,
	basePath: string
)=> {
	const parsedObjs = yaml.loadAll(content) as YamlContent[];
	const updatedObjs = await Promise.all(
		parsedObjs.map(async (obj) => {
			if (isValidAsset(obj))
			{
				return await updateRelativePath(obj, basePath);
			}
			return obj;
		})
	);
	return updatedObjs.map(obj => yaml.dump(obj)) .join('---\n'); 
};

export const resolveRelativePaths = async (zipBuffer: Buffer) => {
	LogWrapper.logDebug('0003', 'Starting path normalization in zip file.');
	const zip = await JSZip.loadAsync(zipBuffer);

	const resolvedZip = new JSZip();
	await Promise.all(
		Object.values(zip.files).map(async (file) => {
			if (file.dir) {
				resolvedZip.folder(file.name);
				LogWrapper.logDebug('0301', 'directory', file.name);
			} else
			{
				const nameArray = file.name.split(path.sep);
				const name = nameArray[nameArray.length - 1];

				if (checkFileExtension(name)) {
					const content = await file.async('string');
					const updatedContent = await updatePathValueInContent(
						content,
						file.name
					);
					resolvedZip.file(file.name,updatedContent);
				} else {
					const fileData = await file.async('nodebuffer');
					resolvedZip.file(file.name, fileData);
				}
			}
		})
	);

	LogWrapper.logDebug('0003', 'Path normalization completed.');
	return resolvedZip;
};

export const processProjectBuild = async (fileBuffer: Buffer) => 
{
	LogWrapper.logInfo('0003', 'Starting project build processing.');
	const zip = await JSZip.loadAsync(fileBuffer);
	LogWrapper.logDebug('0003', 'Loading and normalizing zip paths.');
	const normalizedZip = await normalizeZipPaths(zip);
	const normalizedBuffer = await normalizedZip.generateAsync({ type: 'nodebuffer' });
	const resolvedZip = await resolveRelativePaths(normalizedBuffer);
	LogWrapper.logDebug('0003', 'Normalized zip buffer generated.');
	const obj = new BuildProjectAssets();
	LogWrapper.logInfo('0003', 'Processing normalized project zip.');
	const result = await obj.processProjectZip(await resolvedZip.generateAsync({
		type: 'nodebuffer',
	}));
	if (!result) {
		LogWrapper.logError('0003', 'Project build processing failed.');
		throw new DeployValidationException('Asset Validation Failed For Deloyment');
	}
	LogWrapper.logInfo('0003', 'Project build processing succeeded.');
	return result.generateAsync({ type: 'nodebuffer' });
};
