import { join } from 'path';
import { downloadPlugins, extractPlugins, localPluginsVersion } from './bundleDefaultPlugins';
import { pathExists, readFile, remove } from 'fs-extra';
import Setting from '@joplin/lib/models/Setting';
import { createTempDir, supportDir } from '@joplin/lib/testing/test-utils';
import { rootDir } from './tool-utils';

const fetch = require('node-fetch');

jest.mock('node-fetch', ()=>jest.fn());

const manifests = {
	'io.github.jackgruber.backup': {
		'manifest_version': 1,
		'id': 'io.github.jackgruber.backup',
		'app_min_version': '2.1.3',
		'version': '1.1.0',
		'name': 'Simple Backup',
		'description': 'Plugin to create manual and automatic backups.',
		'author': 'JackGruber',
		'homepage_url': 'https://github.com/JackGruber/joplin-plugin-backup/blob/master/README.md',
		'repository_url': 'https://github.com/JackGruber/joplin-plugin-backup',
		'keywords': [
			'backup',
			'jex',
			'export',
			'zip',
			'7zip',
			'encrypted',
		],
		'_publish_hash': 'sha256:8d8c6a3bb92fafc587269aea58b623b05242d42c0766a05bbe25c3ba2bbdf8ee',
		'_publish_commit': 'master:00ed52133c659e0f3ac1a55f70b776c42fca0a6d',
		'_npm_package_name': 'joplin-plugin-backup',
	},
	'plugin.calebjohn.rich-markdown': {
		'manifest_version': 1,
		'id': 'plugin.calebjohn.rich-markdown',
		'app_min_version': '2.7',
		'version': '0.9.0',
		'name': 'Rich Markdown',
		'description': 'Helping you ditch the markdown viewer for good.',
		'author': 'Caleb John',
		'homepage_url': 'https://github.com/CalebJohn/joplin-rich-markdown#readme',
		'repository_url': 'https://github.com/CalebJohn/joplin-rich-markdown',
		'keywords': [
			'editor',
			'visual',
		],
		'_publish_hash': 'sha256:95337a3868aebdc9bf8c347a37460d0c2753b391ff51a0c72bdccdef9679705f',
		'_publish_commit': 'main:af3493b6ca96c931327ab3bd04906faaed0c782c',
		'_npm_package_name': 'joplin-plugin-rich-markdown',
	},

};

const NPM_Response1 = JSON.stringify({
	'_id': 'joplin-plugin-rich-markdown',
	'name': 'joplin-plugin-rich-markdown',
	'versions': {
		'0.8.2': {
			'name': 'joplin-plugin-rich-markdown',
			'version': '0.8.2',
			'description': 'A plugin that will finally allow you to ditch the markdown viewer, saving space and making your life easier.',
			'_id': 'joplin-plugin-rich-markdown@0.1.0',
			'dist': {
				'tarball': 'no-link-here',
			},
		},
		'0.9.0': {
			'name': 'joplin-plugin-rich-markdown',
			'version': '0.9.0',
			'dist': {
				'tarball': 'response-1-link',
			},
		},
	},
});

const NPM_Response2 = JSON.stringify({
	'_id': 'io.github.jackgruber.backup',
	'name': 'joplin-plugin-rich-markdown',
	'versions': {
		'1.0.0': {
			'name': 'joplin-plugin-rich-markdown',
			'version': '1.0.0',
			'description': 'A plugin that will finally allow you to ditch the markdown viewer, saving space and making your life easier.',
			'_id': 'joplin-plugin-rich-markdown@0.1.0',
			'dist': {
				'tarball': 'no-link-here',
			},
		},
		'1.1.0': {
			'name': 'joplin-plugin-rich-markdown',
			'version': '1.1.0',
			'dist': {
				'tarball': 'response-2-link',
			},
		},
	},
});

async function mockPluginData() {
	const filePath = join(__dirname, '..', 'app-cli', 'tests', 'services', 'plugins', 'mockData', 'mockPlugin.tgz');
	const tgzData = await readFile(filePath, 'utf8');
	return tgzData;
}

describe('bundleDefaultPlugins', () => {

	const testDefaultPluginsInfo = {
		'plugin.calebjohn.rich-markdown': {
			version: '0.9.0',
		},
		'io.github.jackgruber.backup': {
			version: '1.1.0',
			settings: {
				'path': `${Setting.value('profileDir')}`,
			},
		},
	};

	it('should get local plugin versions', async () => {
		const manifestsPath = join(supportDir, 'pluginRepo', 'plugins');
		const testDefaultPluginsInfo = {
			'joplin.plugin.ambrt.backlinksToNote': { version: '1.0.4' },
			'org.joplinapp.plugins.ToggleSidebars': { version: '1.0.2' },
		};
		const localPluginsVersions = await localPluginsVersion(manifestsPath, testDefaultPluginsInfo);

		expect(localPluginsVersions['joplin.plugin.ambrt.backlinksToNote']).toBe('1.0.4');
		expect(localPluginsVersions['org.joplinapp.plugins.ToggleSidebars']).toBe('1.0.2');
	});

	it('should download plugins folder from GitHub with no initial plugins', async () => {

		const testCases = [
			{
				localVersions: { 'io.github.jackgruber.backup': '0.0.0', 'plugin.calebjohn.rich-markdown': '0.0.0' },
				downloadedPlugin1: 'joplin-plugin-rich-markdown-0.9.0.tgz',
				downloadedPlugin2: 'joplin-plugin-backup-1.1.0.tgz',
				numberOfCalls: 4,
				calledWith: ['https://registry.npmjs.org/joplin-plugin-rich-markdown', 'response-1-link', 'https://registry.npmjs.org/joplin-plugin-backup', 'response-2-link'],
			},
			{
				localVersions: { 'io.github.jackgruber.backup': '1.1.0', 'plugin.calebjohn.rich-markdown': '0.0.0' },
				downloadedPlugin1: 'joplin-plugin-rich-markdown-0.9.0.tgz',
				downloadedPlugin2: undefined,
				numberOfCalls: 2,
				calledWith: ['https://registry.npmjs.org/joplin-plugin-rich-markdown', 'response-1-link'],
			},
			{
				localVersions: { 'io.github.jackgruber.backup': '1.1.0', 'plugin.calebjohn.rich-markdown': '0.9.0' },
				downloadedPlugin1: undefined,
				downloadedPlugin2: undefined,
				numberOfCalls: 0,
				calledWith: [],
			},
		];

		const tgzData = await mockPluginData();

		const mockFetch = fetch as jest.MockedFunction<typeof fetch>;

		for (const testCase of testCases) {

			mockFetch.mockResolvedValueOnce({ text: () => Promise.resolve(NPM_Response1), ok: true })
				.mockResolvedValueOnce({ buffer: () => Promise.resolve(tgzData), ok: true })
				.mockResolvedValueOnce({ text: () => Promise.resolve(NPM_Response2), ok: true })
				.mockResolvedValueOnce({ buffer: () => Promise.resolve(tgzData), ok: true });

			const downloadedPlugins = await downloadPlugins(testCase.localVersions, testDefaultPluginsInfo, manifests);

			expect(downloadedPlugins[Object.keys(testDefaultPluginsInfo)[0]]).toBe(testCase.downloadedPlugin1);
			expect(downloadedPlugins[Object.keys(testDefaultPluginsInfo)[1]]).toBe(testCase.downloadedPlugin2);

			expect(mockFetch).toHaveBeenCalledTimes(testCase.numberOfCalls);

			// eslint-disable-next-line github/array-foreach -- Old code before rule was applied
			testCase.calledWith.forEach((callValue, index) => expect(mockFetch).toHaveBeenNthCalledWith(index + 1, callValue));

			jest.clearAllMocks();
		}

		await remove(`${rootDir}/packages/tools/joplin-plugin-backup-1.1.0.tgz`);
		await remove(`${rootDir}/packages/tools/joplin-plugin-rich-markdown-0.9.0.tgz`);
	});

	it('should extract plugins files', async () => {

		const downloadedPluginsNames = { 'plugin.calebjohn.rich-markdown': 'mockPlugin.tgz' };

		const filePath = join(__dirname, '..', 'app-cli', 'tests', 'services', 'plugins', 'mockData');
		const tempDir = await createTempDir();

		await extractPlugins(filePath, tempDir, downloadedPluginsNames);

		expect(await pathExists(join(tempDir, 'plugin.calebjohn.rich-markdown', 'plugin.jpl'))).toBe(true);
		expect(await pathExists(join(tempDir, 'plugin.calebjohn.rich-markdown', 'manifest.json'))).toBe(true);

		await remove(tempDir);
	});

});