import path from 'path';
import fs from 'fs';
import {BuildProjectAssets} from '../../src/build-project-assets.js';
import {ProjectAssetValidator} from '../../src/validator/asset-validator.js';
import JSZip from 'jszip';
import {normalizeZipPaths, resolveRelativePaths} from '../../src/index.js';

jest.mock('../../src/service/log-wrapper.ts');
jest.mock('@apic/studio-shared', () => ({
	ErrorResponse: jest.fn(),
	Metadata_Ref: jest.fn(),
	SpecObject: jest.fn(),
	YamlContent: jest.fn(),
	UpperCaseKinds: jest.fn(),
}));

describe('Build Asset Project Modules', () => {
	it('should validate that given entry and return true if it is yaml  ', async () => {
		const zipFilePath = path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip');
		const Buffer = fs.readFileSync(zipFilePath);
		const obj = new BuildProjectAssets();
		const zip2 = await JSZip.loadAsync(Buffer);
		const normalizedBuffer=await normalizeZipPaths(zip2);
		const buffer2=await normalizedBuffer.generateAsync({ type: 'nodebuffer' });
		const result=await obj['loadZipFromBuffer'](buffer2);

		const entry = result.files[path.normalize('project1/api.yaml')];
		const obj2 = new ProjectAssetValidator();
		const bool=obj2['isYamlFileForFolder'](entry,'project1');
		expect(bool).toBe(true);
	});
	it('should load zip from buffer ', async () => {
		const zipFilePath = path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip');
		const Buffer = fs.readFileSync(zipFilePath);
		const obj = new ProjectAssetValidator();
		const result=await obj['loadZipFromBuffer'](Buffer);
		expect(result).not.toBe(undefined);
		expect(result).not.toBe(null);
		expect(result).not.toBe(false);
	});
	it('should create asset reference map for the project', async () => {
		const zipFilePath = path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip');
		const Buffer = fs.readFileSync(zipFilePath);
		const obj = new ProjectAssetValidator();
		const allFolderNames = new Set<string>();
		allFolderNames.add('project1');
		allFolderNames.add('project2');
		const result=await obj['createProjectAssetReferenceMap'](Buffer,'project1',allFolderNames);
		for (const value of result.values()) {
			expect(value).toBe(true);
		}
	});
	it('should execute catch if createProjectAssetReferenceMap throws an error', async () => {
		const spy = jest.spyOn(BuildProjectAssets.prototype, 'createVersionProcessingMap').mockImplementation(() => {
			throw new Error('Error processing zip');
		});

		const buffer = fs.readFileSync(path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip'));
		const obj = new ProjectAssetValidator();
		const allFolderNames = new Set(['project1', 'project2']);
		const result = await obj['createProjectAssetReferenceMap'](buffer, 'project1', allFolderNames);
	
		expect(result).toBeInstanceOf(Map);
		spy.mockRestore();

	});
	it('should process other folders if unresolved refs exist', async () => {
		const buffer = fs.readFileSync(path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip'));
		const obj = new ProjectAssetValidator();
		const allFolderNames = new Set(['project1', 'project2']);
	
		const processedFolders: string[] = [];
	
		const spy = jest.spyOn(ProjectAssetValidator.prototype as any, 'processYamlFiles')
			.mockImplementation(async function (
				zipContent: unknown,
				folder: unknown,
				refMap: unknown,
				versionMap: unknown,
			) {
				const typedRefMap = refMap as Map<string, boolean>;
				processedFolders.push(folder as string);
	
				if (folder === 'project1') {
					typedRefMap.set('ref1', false); 
				}
			});
	
		const result = await obj['createProjectAssetReferenceMap'](buffer, 'project1', allFolderNames);
	
		expect(result).toBeInstanceOf(Map);
		expect(result.get('ref1')).toBe(false);
	
		expect(processedFolders).toContain('project2');
		spy.mockRestore();
	});

	it('should create asset path reference map for the project', async () => {
		const zipFilePath = path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip');
		const Buffer = fs.readFileSync(zipFilePath);
		const obj = new ProjectAssetValidator();
		const result=await obj['createProjectPathReferenceMap'](Buffer,'project1');
		for (const value of result.values()) {
			expect(value).toBe(false);
		}
	});
	it('should execute catch if createProjectPathReferenceMap throws an error', async () => {
		const spy = jest.spyOn(BuildProjectAssets.prototype, 'createVersionProcessingMap').mockImplementation(() => {
			throw new Error('Error processing zip');
		})

		const buffer = fs.readFileSync(path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip'));
		const obj = new ProjectAssetValidator();
		const result = await obj['createProjectPathReferenceMap'](buffer, 'project1');
		expect(result).toBeInstanceOf(Map);
		spy.mockRestore();
	});
	it('should create asset path reference map for the project and update the map with filepath ', async () => {
		const zipFilePath = path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip');
		const Buffer = fs.readFileSync(zipFilePath);
		const obj = new ProjectAssetValidator();
		const zip2 = await JSZip.loadAsync(Buffer);
		const normalizedBuffer=await normalizeZipPaths(zip2);
		const zip3= await resolveRelativePaths(await normalizedBuffer.generateAsync({ type: 'nodebuffer' }));
		const buffer2=await zip3.generateAsync({ type: 'nodebuffer' });
		const obj2 = new BuildProjectAssets();
		const folderNames = new Set<string>();
		const filePathsInFolder = new Set<string>();
		await obj2['extractFolderNamesAndPaths'](buffer2,folderNames,filePathsInFolder);
		const result=await obj['validateProjectPathReference'](buffer2,'project1',filePathsInFolder);
		expect(result).toBe(true);
	});
	it('should throw an error if ValidateProjectPathReference throws an error', async () => {
		const obj = new ProjectAssetValidator();
		const spy = jest.spyOn(ProjectAssetValidator.prototype as any, 'createProjectPathReferenceMap').mockImplementation(() => {
			throw new Error('Error validating asset');
		})
		const refMap = new Map<string, boolean>();
		const buffer = fs.readFileSync(path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip'));
		const allFolderNames = new Set(['project1', 'project2']);
		const result = await obj['validateProjectPathReference'](buffer, 'project1', allFolderNames);
		expect(result).toBe(false);
		spy.mockRestore();
	})
	it('should throw an error if validateProjectPathReference encounters invalid references', async() => {
		const obj = new ProjectAssetValidator();
		const spy = jest.spyOn(ProjectAssetValidator.prototype as any, 'createProjectPathReferenceMap').mockResolvedValue(new Map([
			['ref1', false],
			['ref2', true],
		]))

		const Buffer = fs.readFileSync(path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip'));
		const filePathsInFolder = new Set<string>();
		const result = await obj['validateProjectPathReference'](Buffer, 'project1', filePathsInFolder);
		expect(result).toBe(false);
	})
	it('should throw an error if ValidateProjectAssetReference encounters invalid references', async () => {
		const obj = new ProjectAssetValidator();
		const spy = jest.spyOn(ProjectAssetValidator.prototype as any, 'createProjectAssetReferenceMap').mockImplementation(() => {
			throw new Error('Error validating asset');
		})
		const refMap = new Map<string, boolean>();
		const buffer = fs.readFileSync(path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip'));
		const allFolderNames = new Set(['project1', 'project2']);
		const result = await obj['validateProjectAssetReference'](buffer, 'project1', allFolderNames);
		expect(result.isValid).toBe(false);
		expect(result.refMap).toBeInstanceOf(Map);
		spy.mockRestore();
	})

	it('should create asset reference map for the project and update the map with filepath ', async () => {
		const zipFilePath = path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip');
		const Buffer = fs.readFileSync(zipFilePath);
		const obj = new ProjectAssetValidator();
		const zip2 = await JSZip.loadAsync(Buffer);
		const normalizedBuffer=await normalizeZipPaths(zip2);
		const zip3= await resolveRelativePaths(await normalizedBuffer.generateAsync({ type: 'nodebuffer' }));
		const buffer2=await zip3.generateAsync({ type: 'nodebuffer' });
		const obj2 = new BuildProjectAssets();
		const folderNames = new Set<string>();
		const filePathsInFolder = new Set<string>();
		await obj2['extractFolderNamesAndPaths'](buffer2,folderNames,filePathsInFolder);
		const result=await obj['validateProjectAssetReference'](buffer2,'project1',filePathsInFolder);
		expect(result.isValid).toBe(true);
		expect(result.refMap).not.toBe(null);
		expect(result.refMap).not.toBe(undefined);
	});

	it('should validate the minimum assets required for deployment ', async () => {
		const zipFilePath = path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip');
		const Buffer = fs.readFileSync(zipFilePath);
		const obj = new ProjectAssetValidator();
		const result=await obj['validateProjectHasMinimumAssets'](Buffer);
		expect(result).toBe(true);
	});
	it('should return false the minimum assets required for deployment is not found ', async () => {
		const zipFilePath = path.resolve(__dirname, '../assets/gateway-project-with-no-valid-assets.zip');
		const Buffer = fs.readFileSync(zipFilePath);
		const obj = new ProjectAssetValidator();
		const result=await obj['validateProjectHasMinimumAssets'](Buffer);
		expect(result).toBe(false);
	});
	it('should validate the api spec for kind api files  ', async () => {
		const zipFilePath = path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip');
		const Buffer = fs.readFileSync(zipFilePath);
		const obj = new ProjectAssetValidator();
		const result=await obj['validateProjectApiSpecVariable'](Buffer,'project1');
		expect(result).toBe(true);
	});
	
	it('should validate the all the yaml contents have their mandatory fields ', async () => {
		const zipFilePath = path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip');
		const Buffer = fs.readFileSync(zipFilePath);
		const obj = new ProjectAssetValidator();
		const result=await obj['validateYaml'](Buffer);
		expect(result).toBe(true);
	});

});
