import AdmZip from 'adm-zip';
import { executeBuildTestAssets } from './projects-asset-testers';
import { showInfo, showError } from '../../helpers/common/message-helper';
import {
	checkForRootDirPermission,
	checkIfAllProjectExists,
	checkIfRootDirExists
} from '../../helpers/apim/root-dir-helper';
import {  searchAsset } from '../../helpers/apim/build-helper';
import { getSubDirectory, normalizePath } from '../../helpers/common/fs-helper';
import { getAPIRefToBuild } from '../../helpers/apim/test-helper';
import { cropPrefix } from '../../helpers/common/string-helper';
import { KindEnums } from '@apic/api-model/common/StudioEnums';

jest.mock('adm-zip', () => {
	return jest.fn().mockImplementation(() => ({
		addLocalFile: jest.fn(),
		toBuffer: jest.fn().mockReturnValue(Buffer.from('mocked-buffer')),
	}));
});

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

jest.mock('../../helpers/apim/root-dir-helper.js', () => ({
	checkForRootDirPermission: jest.fn(),
	checkIfAllProjectExists: jest.fn(),
	checkIfRootDirExists: jest.fn(),
}));

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

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

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

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

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

describe('Project Asset Testers test suite', () => {
	beforeEach(() => {
		(searchAsset as jest.Mock).mockReset();
		(showError as jest.Mock).mockReset();
		jest.clearAllMocks();

		jest.clearAllMocks();
		(AdmZip as jest.Mock).mockClear();
		(AdmZip as jest.Mock).mockImplementation(() => ({
			addLocalFile: jest.fn(),
			toBuffer: jest.fn().mockReturnValue(Buffer.from('mocked-buffer')),
		}));

		(checkIfRootDirExists as jest.Mock).mockReturnValue(true);
		(checkForRootDirPermission as jest.Mock).mockReturnValue(true);
		(checkIfAllProjectExists as jest.Mock).mockReturnValue(true);

		(getSubDirectory as jest.Mock).mockReturnValue('/path/to/root/project1');
		(normalizePath as jest.Mock).mockImplementation((path: string) => path);
		(getAPIRefToBuild as jest.Mock).mockReturnValue(['apiRef']);
		(cropPrefix as jest.Mock).mockReturnValue('tests');
	});

	test('should build the zip file with correct assets and return zipBuffer and apiReference', async () => {
		const rootDirPath = '/path/to/root';
		const project = 'project1';
		const assets = 'testAsset1,testAsset2';
		const mockAddLocalFile = jest.fn();
		(AdmZip as jest.Mock).mockImplementation(() => ({
			addLocalFile: mockAddLocalFile,
			toBuffer: jest.fn().mockReturnValue(Buffer.from('mocked-buffer')),
		}));

		(searchAsset as jest.Mock).mockImplementation((kind, asset) => ({
			name: asset,
			parentPath: '/path/to/root/project1/tests'
		}));

		const result = await executeBuildTestAssets(rootDirPath, project, assets);

		expect(showInfo).toHaveBeenNthCalledWith(1, 'Checking local directory...');
		expect(showInfo).toHaveBeenNthCalledWith(2, 'Checking for projects... ');
		expect(showInfo).toHaveBeenNthCalledWith(3, 'Asset Added :  testAsset1');
		expect(showInfo).toHaveBeenNthCalledWith(4, 'API Dependency identified:  \'apiRef\' ');
		expect(showInfo).toHaveBeenNthCalledWith(5, 'Asset Added :  testAsset2');
		expect(showInfo).toHaveBeenNthCalledWith(6, 'API Dependency identified:  \'apiRef\' ');

		expect(mockAddLocalFile).toHaveBeenCalledTimes(2);
		expect(mockAddLocalFile).toHaveBeenCalledWith('/path/to/root/project1/tests/testAsset1', 'tests');
		expect(mockAddLocalFile).toHaveBeenCalledWith('/path/to/root/project1/tests/testAsset2', 'tests');

		expect(result.zipBuffer).toEqual(Buffer.from('mocked-buffer'));
		expect(result.apiReference).toEqual('apiRef,apiRef');
	});

	test('should throw an error and show an error message when an asset is not found', async () => {
		const rootDirPath = '/path/to/root/project1';
		const project = 'project1';
		const assets = 'testAsset1';
		
		(searchAsset as jest.Mock).mockReturnValueOnce(null);
		(showError as jest.Mock).mockImplementation(() => { });
		
		await expect(executeBuildTestAssets(rootDirPath, project, assets))
			.rejects
			.toThrow('Failed to build test assets');
		
		expect(searchAsset).toHaveBeenCalledWith(KindEnums.Test, 'testAsset1', rootDirPath);
		expect(searchAsset).toHaveBeenCalledTimes(2);
		
		expect(showError).toHaveBeenCalledWith('Asset testAsset1 not found in the project directory.');
	});
	
	test('should throw an error and show a message when API reference is not found', async () => {
		const rootDirPath = '/path/to/root/project1';
		const project = 'project1';
		const assets = 'testAsset1';
		
		(getAPIRefToBuild as jest.Mock).mockReturnValueOnce(null);
		
		await expect(executeBuildTestAssets(rootDirPath, project, assets))
			.rejects
			.toThrow('Failed to build test assets');
	
		expect(showError).toHaveBeenCalledWith('Invalid asset of kind \'TEST\' : testAsset1');
		expect(showError).toHaveBeenCalledWith('Asset testAsset1 not found in the project directory.');
	});
	
	test('should handle exceptions and rethrow an error with a specific message', async () => {
		const rootDirPath = '/root/dir';
		const project = 'project1';
		const assets = 'testAsset1';
		(checkIfRootDirExists as jest.Mock).mockImplementationOnce(() => {
			throw new Error('Some Error');
		});

		await expect(executeBuildTestAssets(rootDirPath, project, assets))
			.rejects
			.toThrow('Failed to build test assets');

		expect(showError).toHaveBeenCalledWith('Some Error');
	});
});
