'use strict';
const fileType = require('file-type');
const getStream = require('get-stream');
const pify = require('pify');
const yauzl = require('yauzl');

const getType = (entry, mode) => {
	const IFMT = 61440;
	const IFDIR = 16384;
	const IFLNK = 40960;
	const madeBy = entry.versionMadeBy >> 8;

	if ((mode & IFMT) === IFLNK) {
		return 'symlink';
	}

	if ((mode & IFMT) === IFDIR || (madeBy === 0 && entry.externalFileAttributes === 16)) {
		return 'directory';
	}

	return 'file';
};

const extractEntry = (entry, zip) => {
	const file = {
		mode: (entry.externalFileAttributes >> 16) & 0xFFFF,
		mtime: entry.getLastModDate(),
		path: entry.fileName
	};

	file.type = getType(entry, file.mode);

	if (file.mode === 0 && file.type === 'directory') {
		file.mode = 493;
	}

	if (file.mode === 0) {
		file.mode = 420;
	}

	return pify(zip.openReadStream.bind(zip))(entry)
		.then(getStream.buffer)
		.then(buf => {
			file.data = buf;

			if (file.type === 'symlink') {
				file.linkname = buf.toString();
			}

			return file;
		})
		.catch(err => {
			zip.close();
			throw err;
		});
};

const extractFile = zip => new Promise((resolve, reject) => {
	const files = [];

	zip.readEntry();

	zip.on('entry', entry => {
		extractEntry(entry, zip)
			.catch(reject)
			.then(file => {
				files.push(file);
				zip.readEntry();
			});
	});

	zip.on('error', reject);
	zip.on('end', () => resolve(files));
});

module.exports = () => buf => {
	if (!Buffer.isBuffer(buf)) {
		return Promise.reject(new TypeError(`Expected a Buffer, got ${typeof buf}`));
	}

	if (!fileType(buf) || fileType(buf).ext !== 'zip') {
		return Promise.resolve([]);
	}

	return pify(yauzl.fromBuffer)(buf, {lazyEntries: true}).then(extractFile);
};
