/**
 * @license
 * Copyright 2020 Balena Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { expect } from 'chai';
import * as _ from 'lodash';
import * as path from 'path';
import * as tar from 'tar-stream';

import { tarDirectory } from '../../build/utils/compose_ts';
import { setupDockerignoreTestData } from '../projects';

const repoPath = path.normalize(path.join(__dirname, '..', '..'));
const projectsPath = path.join(repoPath, 'tests', 'test-data', 'projects');

interface TarFiles {
	[name: string]: {
		fileSize?: number;
		type?: string | null;
	};
}

describe('compare new and old tarDirectory implementations', function () {
	const extraContent = 'extra';
	const extraEntry: tar.Headers = {
		name: 'extra.txt',
		size: extraContent.length,
		type: 'file',
	};
	const preFinalizeCallback = (pack: tar.Pack) => {
		pack.entry(extraEntry, extraContent);
	};

	this.beforeAll(async () => {
		await setupDockerignoreTestData();
	});

	this.afterAll(async () => {
		await setupDockerignoreTestData({ cleanup: true });
	});

	// NOTE: if this test or other tests involving symbolic links fail on Windows
	// (with a mismatched fileSize 13 vs 5 for 'symlink-a.txt'), ensure that the
	// `core.symlinks` property is set to `true` in the `.git/config` file. Ref:
	// https://git-scm.com/docs/git-config#Documentation/git-config.txt-coresymlinks
	it('should produce the expected file list', async function () {
		const dockerignoreProjDir = path.join(
			projectsPath,
			'no-docker-compose',
			'dockerignore1',
		);
		const expectedFiles = {
			'.balena/balena.yml': { fileSize: 12, type: 'file' },
			'.dockerignore': { fileSize: 438, type: 'file' },
			'.gitignore': { fileSize: 20, type: 'file' },
			'.git/foo.txt': { fileSize: 4, type: 'file' },
			'a.txt': { fileSize: 1, type: 'file' },
			'c.txt': { fileSize: 1, type: 'file' },
			Dockerfile: { fileSize: 13, type: 'file' },
			'extra.txt': { fileSize: 5, type: 'file' },
			'src/.balena/balena.yml': { fileSize: 16, type: 'file' },
			'src/.gitignore': { fileSize: 10, type: 'file' },
			'src/src-a.txt': { fileSize: 5, type: 'file' },
			'src/src-c.txt': { fileSize: 5, type: 'file' },
			'vendor/.git/vendor-git-contents': { fileSize: 20, type: 'file' },
		};

		const tarPack = await tarDirectory(dockerignoreProjDir, {
			preFinalizeCallback,
		});
		const fileList = await getTarPackFiles(tarPack);

		expect(fileList).to.deep.equal(expectedFiles);
	});

	it('should produce the expected file list (symbolic links)', async function () {
		const projectPath = path.join(
			projectsPath,
			'no-docker-compose',
			'dockerignore2',
		);
		const expectedFiles = {
			'.dockerignore': { fileSize: 33, type: 'file' },
			'b.txt': { fileSize: 1, type: 'file' },
			Dockerfile: { fileSize: 13, type: 'file' },
			'lib/.dockerignore': { fileSize: 10, type: 'file' },
			'lib/src-b.txt': { fileSize: 5, type: 'file' },
			'src/src-b.txt': { fileSize: 5, type: 'file' },
			'symlink-a.txt': { fileSize: 5, type: 'file' },
		};

		const tarPack = await tarDirectory(projectPath, {});
		const fileList = await getTarPackFiles(tarPack);

		expect(fileList).to.deep.equal(expectedFiles);
	});
});

async function getTarPackFiles(
	pack: import('stream').Readable,
): Promise<TarFiles> {
	const { drainStream } = await import('tar-utils');
	const fileList: TarFiles = {};
	const extract = tar.extract();

	return await new Promise((resolve, reject) => {
		extract
			.on('error', reject)
			.on('entry', async function (header, stream, next) {
				expect(fileList).to.not.have.property(header.name);
				fileList[header.name] = {
					fileSize: header.size,
					type: header.type,
				};
				await drainStream(stream);
				next();
			})
			.on('finish', function () {
				resolve(fileList);
			});
		pack.pipe(extract);
	});
}
