import * as React from 'react';
import { useState, useEffect } from 'react';
import { _ } from '@joplin/lib/locale';
import DialogButtonRow from './DialogButtonRow';
const { themeStyle } = require('@joplin/lib/theme');
const Countable = require('@joplin/lib/countable/Countable');
import markupLanguageUtils from '@joplin/lib/utils/markupLanguageUtils';
import Dialog from '@joplin/lib/components/Dialog';

interface NoteContentPropertiesDialogProps {
	themeId: number;
	text: string;
	markupLanguage: number;
	onClose: ()=> void;
}

interface TextPropertiesMap {
	[key: string]: number;
}

interface KeyToLabelMap {
	[key: string]: string;
}

let markupToHtml_: ReturnType<typeof markupLanguageUtils.newMarkupToHtml> = null;
function markupToHtml() {
	if (markupToHtml_) return markupToHtml_;
	markupToHtml_ = markupLanguageUtils.newMarkupToHtml();
	return markupToHtml_;
}

interface CounterResult {
	words: number;
	all: number;
	characters: number;
}

function countElements(text: string, wordSetter: React.Dispatch<React.SetStateAction<number>>, characterSetter: React.Dispatch<React.SetStateAction<number>>, characterNoSpaceSetter: React.Dispatch<React.SetStateAction<number>>, cjkCharacterSetter: React.Dispatch<React.SetStateAction<number>>, lineSetter: React.Dispatch<React.SetStateAction<number>>) {
	Countable.count(text, (counter: CounterResult) => {
		wordSetter(counter.words);
		characterSetter(counter.all);
		characterNoSpaceSetter(counter.characters);
	});
	const cjkMatches = text.match(/[\p{Script=Han}\p{Script=Bopomofo}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/gu);
	cjkCharacterSetter(cjkMatches ? cjkMatches.length : 0);
	lineSetter(text === '' ? 0 : text.split('\n').length);
}

function formatReadTime(readTimeMinutes: number) {
	if (readTimeMinutes < 1) {
		return '< 1';
	}

	return Math.ceil(readTimeMinutes).toString();
}

export default function NoteContentPropertiesDialog(props: NoteContentPropertiesDialogProps) {
	const theme = themeStyle(props.themeId);
	const tableBodyComps: React.JSX.Element[] = [];
	// For the source Markdown
	const [lines, setLines] = useState<number>(0);
	const [words, setWords] = useState<number>(0);
	const [characters, setCharacters] = useState<number>(0);
	const [charactersNoSpace, setCharactersNoSpace] = useState<number>(0);
	const [cjkCharacters, setCjkCharacters] = useState<number>(0);
	// For source with Markdown syntax stripped out
	const [strippedLines, setStrippedLines] = useState<number>(0);
	const [strippedWords, setStrippedWords] = useState<number>(0);
	const [strippedCharacters, setStrippedCharacters] = useState<number>(0);
	const [strippedCharactersNoSpace, setStrippedCharactersNoSpace] = useState<number>(0);
	const [strippedCjkCharacters, setStrippedCjkCharacters] = useState<number>(0);
	const [strippedReadTime, setStrippedReadTime] = useState<number>(0);
	// This amount based on the following paper:
	// https://www.researchgate.net/publication/332380784_How_many_words_do_we_read_per_minute_A_review_and_meta-analysis_of_reading_rate
	const wordsPerMinute = 250;

	useEffect(() => {
		countElements(props.text, setWords, setCharacters, setCharactersNoSpace, setCjkCharacters, setLines);
	}, [props.text]);

	useEffect(() => {
		const strippedText: string = markupToHtml().stripMarkup(props.markupLanguage, props.text);
		countElements(strippedText, setStrippedWords, setStrippedCharacters, setStrippedCharactersNoSpace, setStrippedCjkCharacters, setStrippedLines);
		// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
	}, [props.text]);

	useEffect(() => {
		const readTimeMinutes: number = strippedWords / wordsPerMinute;
		setStrippedReadTime(readTimeMinutes);
	}, [strippedWords]);

	const textProperties: TextPropertiesMap = {
		lines: lines,
		words: words,
		characters: characters,
		charactersNoSpace: charactersNoSpace,
		cjkCharacters: cjkCharacters,
	};

	const strippedTextProperties: TextPropertiesMap = {
		// The function stripMarkup() currently removes all new lines so we can't use the
		// strippedLines property. Instead we simply use the lines property which should
		// be a good approximation anyway.
		// Also dummy check to silence TypeScript warning
		lines: strippedLines === -5000 ? strippedLines : lines,
		words: strippedWords,
		characters: strippedCharacters,
		charactersNoSpace: strippedCharactersNoSpace,
		cjkCharacters: strippedCjkCharacters,
	};

	const keyToLabel: KeyToLabelMap = {
		words: _('Words'),
		characters: _('Characters'),
		charactersNoSpace: _('Characters excluding spaces'),
		cjkCharacters: _('Chinese/Japanese/Korean characters'),
		lines: _('Lines'),
	};

	const buttonRow_click = () => {
		props.onClose();
	};

	const labelCompStyle = {
		...theme.textStyle,
		fontWeight: 'bold',
		width: '10em',
	};

	const controlCompStyle = {
		...theme.textStyle,
		textAlign: 'center',
	};

	const createTableBodyRow = (key: string, value: number, strippedValue: number) => {
		const labelComp = <td style={labelCompStyle}>{keyToLabel[key]}</td>;
		const controlComp = <td style={controlCompStyle}>{value}</td>;
		const strippedControlComp = <td style={controlCompStyle}>{strippedValue}</td>;

		return (
			<tr key={key}>{labelComp}{controlComp}{strippedControlComp}</tr>
		);
	};

	const tableHeaderStyle = {
		...theme.textStyle,
		textAlign: 'center',
	};

	const tableHeader = (
		<tr>
			<th style={tableHeaderStyle}></th>
			<th style={tableHeaderStyle}>{_('Editor')}</th>
			<th style={tableHeaderStyle}>{_('Viewer')}</th>
		</tr>
	);

	for (const key in textProperties) {
		if (key === 'cjkCharacters' && textProperties[key] === 0 && strippedTextProperties[key] === 0) continue;
		const comp = createTableBodyRow(key, textProperties[key], strippedTextProperties[key]);
		tableBodyComps.push(comp);
	}

	const dialogBoxHeadingStyle = {
		...theme.dialogTitle,
		textAlign: 'center',
	};

	const readTimeLabel = _('Read time: %s min', formatReadTime(strippedReadTime));

	return (
		<Dialog onCancel={props.onClose}>
			<div style={dialogBoxHeadingStyle}>{_('Statistics')}</div>
			<table>
				<thead>
					{tableHeader}
				</thead>
				<tbody>
					{tableBodyComps}
				</tbody>
			</table>
			<div style={{ ...labelCompStyle, marginTop: 10 }}>
				{readTimeLabel}
			</div>
			<DialogButtonRow
				themeId={props.themeId}
				onClick={buttonRow_click}
				okButtonShow={false}
				cancelButtonLabel={_('Close')}
			/>
		</Dialog>
	);
}
