class EdsCCopyToClipboard {

	constructor(wrapper) {
		if (!(navigator.clipboard && typeof navigator.clipboard.writeText === 'function') && !document.execCommand) {
			return;
		}
		
		if (!wrapper) {
			console.error('Could not init copyToClipboard component. Wrapper not found.');
			return;
		}

		const control = wrapper.querySelector('[data-component-copytoclipboard-control]');

		// The `componentUID` differentiates one component from another, when there are many on
		// the page.
		this.componentUID = wrapper.dataset.componentCopytoclipboardWrapper;

		if (!this.componentUID) {
			console.error('Could not init copyToClipboard component. Missing `componentUID`.');
			return;
		}

		this.liveRegion = document.querySelector(`[data-component-copytoclipboard-live-region=${this.componentUID}]`);
		this.input = document.querySelector(`[data-component-copytoclipboard-text-to-copy=${this.componentUID}]`);
		this.template = wrapper.querySelector('[data-button-template]');

		if (!this.liveRegion || !this.input || !control || !this.template) {
			console.error('Could not init copyToClipboard component. Missing core elements.');
			return;
		}

		// Create button from button template
		this.button = this.template.content.cloneNode(true);
		control.appendChild(this.button);
		this.button = control.querySelector('button');

		const {uiFeedbackDuration} = wrapper.dataset;

		const {
			buttonTextOnSuccess,
			buttonTextOnError,
			componentCopytoclipboardButtonAriaLabelledby,
			componentCopytoclipboardButtonAriaLabel
		} = control.dataset;

		const {
			liveRegionTextOnSuccess,
			liveRegionTextOnError
		} = this.liveRegion.dataset;

		this.liveRegionRenderedTextOnSuccess = liveRegionTextOnSuccess || 'copied';
		this.liveRegionRenderedTextOnError = liveRegionTextOnError || 'not copied, please copy manually';

		if (buttonTextOnSuccess && buttonTextOnError) {
			this.displayFeedbackOnButton = true;
			this.buttonRenderedTextOnSuccess = buttonTextOnSuccess;
			this.buttonRenderedTextOnError = buttonTextOnError;
		}
		this.buttonRenderedText = this.button.textContent;
		this.uiFeedbackDuration = parseIntWithDefault(uiFeedbackDuration);

		if (componentCopytoclipboardButtonAriaLabelledby) {
			this.button.setAttribute(
				'aria-labelledby',
				componentCopytoclipboardButtonAriaLabelledby
			);
		} else if (componentCopytoclipboardButtonAriaLabel) {
			this.button.setAttribute(
				'aria-label',
				componentCopytoclipboardButtonAriaLabel
			);
		}

		this._copyToClipboardClickHandler = this._copyToClipboardClickHandler.bind(this);

		// Mouse interactions
		this.button.addEventListener('click', this._copyToClipboardClickHandler);

		// Keyboard interactions
		this.button.addEventListener('keydown', (event) => {
			if (event.key === 'Enter') {
				this._copyToClipboardClickHandler(event);
			}
		});
		this.button.addEventListener('keyup', (event) => {
			// Note: Older browsers may return "Spacebar" for hit on the
			// spacebar rather than the standardised " " (Empty space)
			if (event.key === ' ' || event.key === 'Spacebar') {
				this._copyToClipboardClickHandler(event);
			}
		});
	}

	_copyToClipboardClickHandler(event) {
		event.preventDefault();

		let hasCopied = false;

		this._writeInputFieldValueToClipboard()
			.then(hasWrittenInputFieldValueToClipboard => {
				hasCopied = hasWrittenInputFieldValueToClipboard || this._selectAndCopyInputFieldValue();

				this.button.focus();

				const buttonSpan = this.button.querySelector('span');

				this.liveRegion.textContent = hasCopied ? `${this.liveRegionRenderedTextOnSuccess}` : `${this.liveRegionRenderedTextOnError}`;

				if (this.displayFeedbackOnButton) {
					buttonSpan.textContent = hasCopied ? this.buttonRenderedTextOnSuccess : this.buttonRenderedTextOnError;
				}

				setTimeout(() => {
					this.liveRegion.textContent = '';

					if (this.displayFeedbackOnButton) {
						buttonSpan.textContent = this.buttonRenderedText;
					}
				}, this.uiFeedbackDuration);
			});
	}

	/**
	* Selects the instance text input field and executes the 'copy' command.
	*
	* **Note**: This function is meant to be used as a fallback in case there is
	* any issue with the `navigator.clipboard` API.
	*
	* @returns {boolean} `true` if the copy operation is successful, `false` otherwise.
	*/
	_selectAndCopyInputFieldValue() {
		try {
			this.input.select();
			return document.execCommand('copy');
		} catch {
			return false;
		}
	}

	/**
	* Writes the value of the instance input into the clipboard, using the `navigator.clipboard` API.
	*
	* @returns {boolean} `true` if the write operation is successful, `false` otherwise.
	*/
	_writeInputFieldValueToClipboard() {
		let text;

		if (this.input.value !== undefined) {
			text = this.input.value;
		} else if (this.input.innerText !== undefined) {
			text = this.input.innerText;
		}

		return writeTextToClipboard(text)
			.then(() => {
				return true;
			})
			// eslint-disable-next-line no-unused-vars
			.catch(_error => {
				return false;
			});
	}
}

/**
* Writes the given text into the clipboard, using the `navigator.clipboard` API.
*
* @param {string} text - Text to copy into the user's clipboard.
*
* @returns {Promise} Promise that is resolved once the system clipboard has been updated.
* @throws Will throw an error if the write operation fails.
*/
async function writeTextToClipboard(text) {
	try {
		await navigator.clipboard.writeText(text);
	} catch (error) {
		throw new Error(`Could not write to clipboard: ${error.message}`);
	}
}

/**
* Converts a string to integer. Returns a default value of 5000 on parsing error.
*
* @param {string} value - The string value to convert to integer.
* @returns {number} The parsed value (in milliseconds).
*/
function parseIntWithDefault(value) {
	if (typeof value === 'string') {
		const parsedValue = parseInt(value, 10);
		if (!isNaN(parsedValue)) {
			return parsedValue;
		}
	}
	return 5000;
}

export default EdsCCopyToClipboard;
