/**
* Copyright Super iPaaS Integration LLC, an IBM Company 2024
*/
/* eslint-disable @typescript-eslint/no-var-requires */
import AdmZip from 'adm-zip';
import path from 'path';
import { addValidAssetsToZip, checkAndAddValidYamlFile, checkAndAddValidJsonFile, checkAndAddOtherFile } from './zip-helper';
import { isDirOrFileExists, readDirectoryContents, isDirectory, isYamlFile, isJsonFile, readFile, getFileNameFromPath, isOtherFile} from '../../helpers/common/fs-helper';
import { DIRECTORY_DOESNT_EXIST, ASSERT_ADDED } from '../../constants/message-constants.js';
import { showInfo, showWarning } from './message-helper';
import { readMultiYaml, convertToYAMLString } from '../../helpers/common/yaml-helper.js';
import { readJson } from '../../helpers/common/json-helper.js';
import { DebugManager } from "../../debug/debug-manager.js";
import { isValidRestAPI } from './rest-api-validation-helper.js';
import { isValidAsset } from '../apim/asset-helper.js';

jest.mock('../../helpers/common/fs-helper', () => ({
	isDirOrFileExists: jest.fn(),
	readDirectoryContents: jest.fn(),
	isDirectory: jest.fn(),
	isYamlFile: jest.fn(),
	isJsonFile: jest.fn(),
	isOtherFile: jest.fn(),
	readFile: jest.fn(),
	getFileNameFromPath: jest.fn()

}));

jest.mock('../../helpers/common/message-helper', () => ({
	showWarning: jest.fn(),
	showError: jest.fn(),
	showInfo: jest.fn()
}));


jest.mock('../../debug/debug-manager.js', () => ({
	DebugManager: {
		getInstance: () => ({
			isDebugEnabled: jest.fn().mockReturnValue(true),
			setDebugEnabled: jest.fn(),
	  	})
	}
}));

jest.mock('../../helpers/apim/asset-helper.js', () => ({
	isValidAsset: jest.fn(),
}));

jest.mock('../../helpers/common/rest-api-validation-helper.js', () => ({
	isValidRestAPI: jest.fn(),
}));

jest.mock('../../helpers/common/yaml-helper.js', () => ({
	readMultiYaml: jest.fn(),
	convertToYAMLString: jest.fn()
}));

jest.mock('../../helpers/common/json-helper.js', () => ({
	readJson: jest.fn()
}));


describe('Zip Helper Recursive and File Skipping Tests', () => {
	let mockZipFile: jest.Mocked<AdmZip>;

	beforeEach(() => {
		jest.clearAllMocks();
		mockZipFile = new AdmZip() as jest.Mocked<AdmZip>;
		mockZipFile.addLocalFile = jest.fn();
		mockZipFile.addFile = jest.fn();
	});
	afterEach(() => {
		jest.clearAllMocks();
	});

it('should add valid assets recursively and handle directory structure', () => {
	const mockFolderPath = 'folder';
	const mockZipFolderPath = 'zip/folder';
	const mockExcludeKinds: string[] = ['kindToExclude'];
	const mockItems = ['file.yaml', 'subfolder'];

	(readMultiYaml as jest.Mock).mockReturnValue([{ kind: 'api' }, {kind: 'transport'}]);
	(isDirOrFileExists as jest.Mock).mockReturnValue(true);
	(readDirectoryContents as jest.Mock).mockReturnValue(mockItems);
	(isDirectory as jest.Mock).mockImplementation((itemPath) => itemPath === path.join(mockFolderPath, 'subfolder'));
	(isYamlFile as jest.Mock).mockReturnValue(true);
	(isJsonFile as jest.Mock).mockReturnValue(false);

	addValidAssetsToZip(mockFolderPath, mockZipFolderPath, mockZipFile, mockExcludeKinds);
	expect(mockZipFile.addFile).toHaveBeenCalledWith(
		`${mockZipFolderPath}/`,
		Buffer.from("")
	);

	// Check if the YAML file is added
	expect(mockZipFile.addFile).toHaveBeenCalledWith(
		`${mockZipFolderPath}/`,
		expect.any(Buffer)
	);
});
	it('should skip non-supported files', () => {
		const mockFolderPath = 'folder';
		const mockZipFolderPath = 'zip/folder';
		const mockExcludeKinds: string[] = [];
		const mockItems = ['file.txt'];

		(isDirOrFileExists as jest.Mock).mockReturnValue(true);
		(readDirectoryContents as jest.Mock).mockReturnValue(mockItems);
		(isDirectory as jest.Mock).mockReturnValue(false);
		(isYamlFile as jest.Mock).mockReturnValue(false);
		(isJsonFile as jest.Mock).mockReturnValue(false);

		addValidAssetsToZip(mockFolderPath, mockZipFolderPath, mockZipFile, mockExcludeKinds);

		expect(mockZipFile.addFile).not.toHaveBeenCalledWith(
			`${mockZipFolderPath}/file.txt`,
			expect.any(Buffer)
		);
	});
});

describe('Zip Helper Test Suite', () => {
	let mockZipFile: jest.Mocked<AdmZip>;

	beforeEach(() => {
		jest.clearAllMocks();
		mockZipFile = new AdmZip() as jest.Mocked<AdmZip>;
		mockZipFile.addLocalFile = jest.fn();
	});
	afterEach(() => {
		jest.clearAllMocks();
	});

	it.skip('should not add files if directory has no files, but should add the empty directory', () => {
		const mockFolderPath = 'folder';
		const mockZipFolderPath = 'zip/folder';
		const mockExcludeKinds: string[] = ['kindToExclude'];
		
		(isDirOrFileExists as jest.Mock).mockReturnValue(true);
		(readDirectoryContents as jest.Mock).mockReturnValue([]); 
		
		addValidAssetsToZip(mockFolderPath, mockZipFolderPath, mockZipFile, mockExcludeKinds);
		const expectedPath = path.join(mockZipFolderPath, '/');
		
		expect(mockZipFile.getEntries().length).toBe(1);
		expect(mockZipFile.getEntries()[0].entryName).toBe(expectedPath);
	});

	it('should not add files if directory does not exist', () => {
		const mockFolderPath = 'folder';
		const mockZipFolderPath = 'zip/folder';
		const mockExcludeKinds: string[] = ['kindToExclude'];

		(isDirOrFileExists as jest.Mock).mockReturnValue(false);
		(showWarning as jest.Mock).mockImplementation((message) => {
			expect(message).toBe(`${DIRECTORY_DOESNT_EXIST} ${mockFolderPath}`);
		  });
		addValidAssetsToZip(mockFolderPath, mockZipFolderPath, mockZipFile, mockExcludeKinds);
		expect(showWarning).toHaveBeenCalledWith(`${DIRECTORY_DOESNT_EXIST} ${mockFolderPath}`);
		expect(mockZipFile.getEntries().length).toBe(0);
	});

	it('should add yaml files to the zip', () => {
		const mockFolderPath = 'folder';
		const mockZipFolderPath = 'zip/folder';
		const mockExcludeKinds: string[] = ['kindToExclude'];
		const mockYamlContent = 'yaml content';
		const mockCombinedYaml = 'combined yaml content';
		
		(isDirOrFileExists as jest.Mock).mockReturnValue(true);
		(readDirectoryContents as jest.Mock).mockReturnValue(['api.yaml']);
		(isDirectory as jest.Mock).mockReturnValue(false);
		(isYamlFile as jest.Mock).mockReturnValue(true);
		(readFile as jest.Mock).mockReturnValue(mockYamlContent);
		(readMultiYaml as jest.Mock).mockReturnValue([{ kind: 'api' }]);
		(isValidRestAPI as jest.Mock).mockReturnValue(false);
		(isValidAsset as jest.Mock).mockReturnValue(true);
		(convertToYAMLString as jest.Mock).mockReturnValue(mockCombinedYaml);

		checkAndAddValidYamlFile('zip/folder/api.yaml', mockZipFolderPath, mockZipFile, []);
		addValidAssetsToZip(mockFolderPath, mockZipFolderPath, mockZipFile, mockExcludeKinds);		
		expect(mockZipFile.getEntries().length).toBe(1);
		const yamlEntry = mockZipFile.getEntry('zip/folder/api.yaml');
		expect(yamlEntry).toBeDefined();
	});
	it('should handle YAML files with errors gracefully', () => {
        const mockFolderPath = 'folder';
        const mockZipFolderPath = 'zip/folder';
        const mockExcludeKinds: string[] = [];
        const mockFilePath = 'folder/error.yaml';
        
        (isDirOrFileExists as jest.Mock).mockReturnValue(true);
        (readDirectoryContents as jest.Mock).mockReturnValue(['error.yaml']);
        (isDirectory as jest.Mock).mockReturnValue(false);
        (isYamlFile as jest.Mock).mockReturnValue(true);
        (readFile as jest.Mock).mockImplementation(() => { throw new Error('File read error'); });
		expect(() => {
            checkAndAddValidYamlFile(mockFilePath, mockZipFolderPath, mockZipFile, mockExcludeKinds);
        }).toThrow(new Error(`File read error`));
    });

	it('should handle JSON files with errors gracefully', () => {
        const mockFolderPath = 'folder';
        const mockZipFolderPath = 'zip/folder';
        const mockFilePath = 'folder/error.json';
        
        (isDirOrFileExists as jest.Mock).mockReturnValue(true);
        (readDirectoryContents as jest.Mock).mockReturnValue(['error.json']);
        (isDirectory as jest.Mock).mockReturnValue(false);
        (isJsonFile as jest.Mock).mockReturnValue(true);
        (readFile as jest.Mock).mockImplementation(() => { throw new Error('File read error'); });

		expect(() => {
			checkAndAddValidJsonFile(mockFilePath, mockZipFolderPath, mockZipFile, []);
		}).toThrow(new Error(`File read error`));

    });

	test('should handle files that are not JSON or YAML', () => {
		(isDirOrFileExists as jest.Mock).mockReturnValue(true);
		(readDirectoryContents as jest.Mock).mockReturnValue(['other.txt']);
		(isDirectory as jest.Mock).mockReturnValue(false);
		(isYamlFile as jest.Mock).mockReturnValue(false);
		(isJsonFile as jest.Mock).mockReturnValue(false);
		
		addValidAssetsToZip('folder', 'zip/folder', mockZipFile, []);
	
		const entry = mockZipFile.getEntry('zip/folder/other.txt');
		expect(entry).toBeNull();
  	});

	test('should handle multi YAML contents and add it to zip', () => {
	const mockZipFolderPath = 'zip/folder';
	const mockFileName = 'file.yaml';
	const mockExcludeKinds: string[] = ['kindToExclude'];
	const mockYamlContents = [
		{ kind: 'api', name: 'API Asset' },
		{ kind: 'transport', name: 'Transport Asset' },
		{ kind: 'kindToExclude', name: 'Excluded Asset' },
	];
	const convertedYaml = "---\nname: Converted Asset\n";
	(readMultiYaml as jest.Mock).mockReturnValue(mockYamlContents);
	(getFileNameFromPath as jest.Mock).mockReturnValue(mockFileName);
	(isValidRestAPI as jest.Mock).mockReturnValue(false);
	(isValidAsset as jest.Mock).mockReturnValue(true);
	(convertToYAMLString as jest.Mock).mockReturnValue(convertedYaml);

	addValidAssetsToZip('folder', mockZipFolderPath, mockZipFile, mockExcludeKinds);

	expect(mockZipFile.getEntries().length).toBe(1);
	const yamlEntry = mockZipFile.getEntry('zip/folder/file.yaml');
	expect(yamlEntry).toBeDefined();
	});

	it('should add non-YAML/JSON files to the zip', () => {
		const mockZipFolderPath = 'zip/folder';
		const mockExcludeKinds: string[] = ['kindToExclude'];
	
		(isDirOrFileExists as jest.Mock).mockReturnValue(true);
		(readDirectoryContents as jest.Mock).mockReturnValue(['otherFile.txt']);
		(isDirectory as jest.Mock).mockReturnValue(false);
		(isOtherFile as jest.Mock).mockReturnValue(true);
		checkAndAddOtherFile('zip/folder/otherFile.txt', mockZipFolderPath, mockZipFile);
		addValidAssetsToZip('folder', mockZipFolderPath, mockZipFile, mockExcludeKinds);
		console.log(mockZipFile);
		expect(mockZipFile.getEntries().length).toBe(1);
		const otherEntry = mockZipFile.getEntry('zip/folder/otherFile.txt');
  		expect(otherEntry).toBeDefined();
	});

	it('should add json files to the zip', () => {
		const mockFolderPath = 'folder';
		const mockZipFolderPath = 'zip/folder';
		const mockExcludeKinds: string[] = ['kindToExclude'];
		const mockJsonContent = 'json content';
		
		(isDirOrFileExists as jest.Mock).mockReturnValue(true);
		(readDirectoryContents as jest.Mock).mockReturnValue(['swagger.json']);
		(isDirectory as jest.Mock).mockReturnValue(false);
		(isJsonFile as jest.Mock).mockReturnValue(true);
		(readFile as jest.Mock).mockReturnValue(mockJsonContent);
		(readJson as jest.Mock).mockReturnValue({ "swagger": "2.0" });
		(isValidRestAPI as jest.Mock).mockReturnValue(true);

		checkAndAddValidJsonFile('zip/folder/swagger.json', mockZipFolderPath, mockZipFile, []);
		addValidAssetsToZip(mockFolderPath, mockZipFolderPath, mockZipFile, mockExcludeKinds);		
		expect(mockZipFile.getEntries().length).toBe(1);
		const jsonEntry = mockZipFile.getEntry('zip/folder/swagger.json');
  		expect(jsonEntry).toBeDefined();
	});
	

});
