/**
* Copyright Super iPaaS Integration LLC, an IBM Company 2024
*/
import JSZip from 'jszip';
import yaml from 'js-yaml';
import { CollectionCreator } from '../newman/newman-collection.builder.js';
import { AssetParser } from '../parsers/asset.parser.js';
import { addErrorToResponse } from '../helpers/helper.js';
import { AppConstants } from '../constants/app.constants.js';
import { YamlValidator } from '../validator/yaml.validator.js';
import { TestManagerInterface, TestSpec } from '../models/interface.js';
import { NewmanRunner } from '../newman/newman-test.js';
import { GatewayAssetHandler } from '../converter/gateway-asset.handler.js';
import { LogWrapper } from '../service/log-wrapper.js';

export class TestManager implements TestManagerInterface {
	private async validateEndpoints(buffer: Buffer): Promise<boolean> {
		LogWrapper.logInfo('0204', 'endpoint');

		const zipContent = await this.loadZipContent(buffer);

		for (const [, file] of Object.entries(zipContent.files)) {
			if (this.isYamlFile(file.name)) {
				const parsedYamlDatas = await this.parseYamlFile(file);
				const isValid = await this.validateYamlData(parsedYamlDatas, buffer);
				if (!isValid) {
					return false;
				}
			}
		}

		LogWrapper.logInfo('0206', 'Endpoint');
		return true;
	}

	private async loadZipContent(buffer: Buffer): Promise<JSZip> {
		const zip = new JSZip();
		return  zip.loadAsync(buffer);
	}

	private isYamlFile(fileName: string): boolean {
		return fileName.endsWith('.yaml') || fileName.endsWith('.yml');
	}

	private async parseYamlFile(file: JSZip.JSZipObject): Promise<TestSpec[]> {
		const data = await file.async('string');
		return yaml.loadAll(data) as TestSpec[];
	}

	private async validateYamlData(parsedYamlDatas: TestSpec[], buffer: Buffer): Promise<boolean> {
		for (const parsedData of parsedYamlDatas) {
			if (parsedData.kind?.toLowerCase() !== 'test' || !parsedData.kind) {
				continue;
			}

			const apiReference = parsedData.spec.api?.$ref;
			const endpointReference = parsedData.spec.api?.$endpoint;

			if (apiReference) {
				const isValid = await this.validateApiReference(apiReference, buffer);
				if (!isValid) {
					return false;
				}
			} else if (!endpointReference) {
				return false;
			}
		}
		return true;
	}

	private async validateApiReference(apiReference: string, buffer: Buffer): Promise<boolean> {
		const gatewayHandler = new GatewayAssetHandler(buffer);
		const endpoint = await gatewayHandler.getApiEndpoints(apiReference);

		if (endpoint[0].length === 0) {
			addErrorToResponse(AppConstants.VALIDATION_ERROR_CODE, 'endpoint', `Invalid or Empty endpoint for reference ${apiReference}`);
			LogWrapper.logError('0205', apiReference);
			return false;
		}

		return true;
	}


	private async validateReferences(buffer: Buffer) {
		LogWrapper.logInfo('0204', 'reference');
		const obj = new AssetParser();
		const refMap = await obj.createAssetReferenceMap(buffer);
		await obj.updateMapWithGatewayEndpoints(buffer, refMap);

		const allRefsValid = Array.from(refMap.entries()).every(([key, value]) => {
			if (!value) {
				addErrorToResponse(AppConstants.VALIDATION_ERROR_CODE, key, `Validation failed for Reference ${key}`);
				LogWrapper.logError('0207', key);
			}
			return value;
		});

		if (!allRefsValid) {
			LogWrapper.logError('0003', 'Validation failed: Some references are not valid.');
			return false;
		}

		LogWrapper.logInfo('0206', 'Reference');
		return true;
	}

	private async validate(buffer: Buffer): Promise<boolean> {
		LogWrapper.logDebug('0003', 'Starting overall validation process.');
		const validateObj = new YamlValidator();

		if (!await validateObj.validateYamlFiles(buffer)) {
			LogWrapper.logError('0004', 'YAML validation failed.');
			return false;
		}

		if (!await this.validateEndpoints(buffer)) {
			LogWrapper.logError('0004', 'Endpoint validation failed.');
			return false;
		}

		if (!await this.validateReferences(buffer)) {
			LogWrapper.logError('0004', 'Reference validation failed.');
			return false;
		}

		LogWrapper.logInfo('0003', 'Overall validation process completed successfully.');
		return true;
	}

	async processFile(buffer: Buffer) {
		LogWrapper.logInfo('0003', 'Starting file processing.');

		if (!await this.validate(buffer)) {
			LogWrapper.logError('0004', 'File processing aborted due to validation failure.');
			return null;
		}
		const zipContent = await this.loadZipContent(buffer);
		const summaries = [];

		for (const [, file] of Object.entries(zipContent.files)) {
			const data = await file.async('string');
			const parsedYamlDatas = yaml.loadAll(data) as TestSpec[];

			for (const parsedData of parsedYamlDatas) {

				if (parsedData.kind?.toLowerCase() !== 'test') {
					continue;
				}

				LogWrapper.logInfo('0208', file.name);
				const collectionCreator = new CollectionCreator();
				const collection = await collectionCreator.createCollection(parsedData, buffer);
				const runner = new NewmanRunner(collection);
				const summary = await runner.run();
				summaries.push(summary);

				LogWrapper.logInfo('0209', file.name);
			}
		}

		LogWrapper.logInfo('0003', 'File processing completed.');
		return summaries;
	}
}
