/**
* Copyright Super iPaaS Integration LLC, an IBM Company 2024
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import AdmZip from 'adm-zip';
import { getRandomFileName, isDirectory, isDirOrFileExists, normalizePath } from '../common/fs-helper.js';
import { showInfo } from '../common/message-helper.js';
import { addDependencyAsset, isSameAsset, searchAsset, fromAssetRefValue, } from './build-helper.js';
import fs from 'fs';
import { Metadata } from '@apic/api-model/common/Metadata.js';

// Mock implementation
jest.mock('adm-zip', () => {
	let entries: any[] = [];

	const mockAddFile = jest.fn((name: string, data: Buffer) => {
		entries = [...entries, {
			entryName: name,
			getData: () => data,
			isDirectory: false,
		}];
	});

	const mockGetEntries = jest.fn(() => entries);

	return jest.fn().mockImplementation(() => ({
		addFile: mockAddFile,
		getEntries: mockGetEntries,
	}));
});

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

jest.mock('../common/fs-helper', () => ({
	getRandomFileName: jest.fn(),
	isDirectory: jest.fn(),
	isDirOrFileExists: jest.fn(),
	isYamlFile: jest.fn(),
	readFile: jest.fn(),
	normalizePath: jest.fn(),
}));

jest.mock('./asset-helper', () => ({
	findFirstAPIAssetInZip: jest.fn(),
	getTargetModelAssetKind: jest.fn(),
	isValidAsset: jest.fn(),
}));

jest.mock('./asset-kinds/policy-helper', () => ({
	getDeploymentMode: jest.fn(),
}));

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

jest.mock('fs', () => ({
	readdirSync: jest.fn(),
	existsSync: jest.fn(),
	Dirent: jest.fn().mockImplementation((name: string, isDirectory: boolean) => ({
		name,
		isDirectory: () => isDirectory,
	})),
	readFileSync: jest.fn(),
	readfileSync: jest.fn()
}));

jest.mock('./api-build-helper.js', () => ({}))


describe('Build helper function test suite', () => {
	afterEach(() => {
		jest.clearAllMocks();
	});

	describe('addDependencyAsset function', () => {
		let zip: AdmZip;
		beforeEach(() => {
			zip = new AdmZip();
			zip.addLocalFile = jest.fn();
		});

		it('should add a dependency asset to the zip', () => {
			const mockDirent: fs.Dirent = {
				name: 'testName.yml',
				isDirectory: () => false,
				isFile: () => true,
				isBlockDevice: () => false,
				isCharacterDevice: () => false,
				isFIFO: () => false,
				isSocket: () => false,
				isSymbolicLink: () => false,
				[Symbol.toStringTag]: 'Dirent',
				parentPath: '/mock/path',
			} as unknown as fs.Dirent;
			const mockFileName = 'randomFileName.yml';

			(getRandomFileName as jest.Mock).mockReturnValue(mockFileName);
			(normalizePath as jest.Mock).mockReturnValue(`/mock/path/${mockDirent.name}`);

			addDependencyAsset(mockDirent, zip);

			expect(getRandomFileName).toHaveBeenCalledWith('.yml');
			expect(normalizePath).toHaveBeenCalledWith(`/mock/path/${mockDirent.name}`);
			expect(zip.addLocalFile).toHaveBeenCalledWith(`/mock/path/${mockDirent.name}`, 'dependencies', mockFileName);
		});

		it('should not add a null asset to the zip', () => {
			addDependencyAsset(null as unknown as fs.Dirent, zip);

			expect(showInfo).not.toHaveBeenCalled();
			expect(zip.addLocalFile).not.toHaveBeenCalled();
		});

		it('should not add an undefined asset to the zip', () => {
			addDependencyAsset(undefined as unknown as fs.Dirent, zip);

			expect(showInfo).not.toHaveBeenCalled();
			expect(zip.addLocalFile).not.toHaveBeenCalled();
		});

		it('should add a dependency asset to the zip with a different file extension', () => {
			const mockDirent: fs.Dirent = {
				name: 'testName.json',
				isDirectory: () => false,
				isFile: () => true,
				isBlockDevice: () => false,
				isCharacterDevice: () => false,
				isFIFO: () => false,
				isSocket: () => false,
				isSymbolicLink: () => false,
				[Symbol.toStringTag]: 'Dirent',
				parentPath: '/mock/path',
			} as unknown as fs.Dirent;
			const mockFileName = 'randomFileName.json';

			(getRandomFileName as jest.Mock).mockReturnValue(mockFileName);
			(normalizePath as jest.Mock).mockReturnValue(`/mock/path/${mockDirent.name}`);

			addDependencyAsset(mockDirent, zip, '.json');

			expect(getRandomFileName).toHaveBeenCalledWith('.json');
			expect(normalizePath).toHaveBeenCalledWith(`/mock/path/${mockDirent.name}`);
			expect(zip.addLocalFile).toHaveBeenCalledWith(`/mock/path/${mockDirent.name}`, 'dependencies', mockFileName);
		});
	});
	describe('isSameAsset function', () => {
		it('should return true for mixed version assets', () => {
			const metadata1 = { namespace: 'ns', name: 'asset', version: '1' };
			const metadata2 = { namespace: 'ns', name: 'asset', version: '1.0' };

			expect(isSameAsset(metadata1, metadata2)).toBe(true);
		});

		it('should return true for identical assets', () => {
			const metadata1 = { namespace: 'ns', name: 'asset', version: '1.0' };
			const metadata2 = { namespace: 'ns', name: 'asset', version: '1.0' };

			expect(isSameAsset(metadata1, metadata2)).toBe(true);
		});

		it('should return false for different namespace assets', () => {
			const metadata1 = { namespace: 'ns2', name: 'asset', version: '1.0.0' };
			const metadata2 = { namespace: 'ns', name: 'asset', version: '1.0' };

			expect(isSameAsset(metadata1, metadata2)).toBe(false);
		});


		it('should return false for different assets', () => {
			const metadata1: Metadata = { namespace: 'ns', name: 'name', version: '1.0.0' };
			const metadata2: Metadata = { namespace: 'ns', name: 'name2', version: '1.0.0' };

			expect(isSameAsset(metadata1, metadata2)).toBe(false);
		});
	});

	describe('searchAsset function', () => {
		const projectDirPath = '/path/to/project';
		it('should throw an error for invalid directory', () => {
			const projectDirPath = 'invalidPath';

			(isDirOrFileExists as jest.Mock).mockReturnValue(false);

			expect(() => searchAsset('kind', 'ref', projectDirPath)).toThrow(`Invalid directory ${projectDirPath}`);
		});

		it('should return undefined and show warning if no entries found', () => {
			(isDirOrFileExists as jest.Mock).mockReturnValue(true);
			(isDirectory as jest.Mock).mockReturnValue(true);
			(fs.readdirSync as jest.Mock).mockReturnValue([]);

			const result = searchAsset('kind', 'ref', projectDirPath);

			expect(result).toBeUndefined();
		});

	});

	describe('fromAssetRefValue function', () => {
		it('should parse asset ref value with name only', () => {
			const refValue = 'name';

			expect(fromAssetRefValue(refValue)).toEqual({ name: 'name' });
		});

		it('should parse asset ref value with name and version', () => {
			const refValue = 'name:1.0.0';

			expect(fromAssetRefValue(refValue)).toEqual({ name: 'name', version: '1.0.0' });
		});
		it('should handle asset ref value with only colons', () => {
			const refValue = ':::';
			const expectedMetadata: Metadata = { namespace: '', name: '', version: '' };

			const result = fromAssetRefValue(refValue);

			expect(result).toEqual(expectedMetadata);
		});
		it('should parse asset ref value with namespace, name, and version', () => {
			const refValue = 'ns:name:1.0.0';

			expect(fromAssetRefValue(refValue)).toEqual({ namespace: 'ns', name: 'name', version: '1.0.0' });
		});
		it('should handle empty asset ref value', () => {
			const refValue = '';
			const expectedMetadata: Metadata = { name: '' };

			const result = fromAssetRefValue(refValue);

			expect(result).toEqual(expectedMetadata);
		});
	});
});
