/**
 * @license
 * Copyright 2019 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 type { OptionDefinition } from 'capitano';
import * as ent from 'ent';
import * as fs from 'fs';
import * as readline from 'readline';

export function getOptionPrefix(signature: string) {
	if (signature.length > 1) {
		return '--';
	} else {
		return '-';
	}
}

export function getOptionSignature(signature: string) {
	return `${getOptionPrefix(signature)}${signature}`;
}

export function parseCapitanoOption(option: OptionDefinition): string {
	let result = getOptionSignature(option.signature);

	if (Array.isArray(option.alias)) {
		for (const alias of option.alias) {
			result += `, ${getOptionSignature(alias)}`;
		}
	} else if (typeof option.alias === 'string') {
		result += `, ${getOptionSignature(option.alias)}`;
	}

	if (option.parameter) {
		result += ` <${option.parameter}>`;
	}

	return ent.encode(result);
}

export class MarkdownFileParser {
	constructor(public mdFilePath: string) {}

	/**
	 * Extract the lines of a markdown document section with the given title.
	 * For example, consider this sample markdown document:
	 * ```
	 *     # balena CLI
	 *
	 *     ## Introduction
	 *     Lorem ipsum dolor sit amet, consectetur adipiscing elit,
	 *
	 *     ## Getting Started
	 *     sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
	 *
	 *     ### Prerequisites
	 *     - Foo
	 *     - Bar
	 *
	 *     ## Support
	 *     Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.
	 * ```
	 *
	 * Calling getSectionOfTitle('Getting Started') for the markdown doc above
	 * returns everything from line '## Getting Started' (included) to line
	 * '## Support' (excluded). This method counts the number of '#' characters
	 * to determine that subsections should be included as part of the parent
	 * section.
	 *
	 * @param title The section title without '#' chars, eg. 'Getting Started'
	 */
	public async getSectionOfTitle(
		title: string,
		includeSubsections = true,
	): Promise<string> {
		let foundSectionLines: string[];
		let foundSectionLevel = 0;

		const rl = readline.createInterface({
			input: fs.createReadStream(this.mdFilePath),
			crlfDelay: Infinity,
		});

		rl.on('line', (line) => {
			// try to match a line like "## Getting Started", where the number
			// of '#' characters is the sectionLevel ('##' -> 2), and the
			// sectionTitle is "Getting Started"
			const match = /^(#+)\s+(.+)/.exec(line);
			if (match) {
				const sectionLevel = match[1].length;
				const sectionTitle = match[2];

				// If the target section had already been found: append a line, or end it
				if (foundSectionLines) {
					if (!includeSubsections || sectionLevel <= foundSectionLevel) {
						// end previously found section
						rl.close();
					}
				} else if (sectionTitle === title) {
					// found the target section
					foundSectionLevel = sectionLevel;
					foundSectionLines = [];
				}
			}
			if (foundSectionLines) {
				foundSectionLines.push(line);
			}
		});

		return await new Promise((resolve, reject) => {
			rl.on('close', () => {
				if (foundSectionLines) {
					resolve(foundSectionLines.join('\n'));
				} else {
					reject(
						new Error(
							`Markdown section not found: title="${title}" file="${this.mdFilePath}"`,
						),
					);
				}
			});
		});
	}
}