import {
	Html5QrcodeScanner,
	Html5QrcodeSupportedFormats,
	Html5QrcodeScanType,
} from 'html5-qrcode';
// eslint-disable-next-line prettier/prettier
import type { Html5QrcodeResult, Html5QrcodeError } from 'html5-qrcode/core';
import { createPopper } from '@popperjs/core';
import type { Instance as PopperInstance } from '@popperjs/core/lib/types';

const icon = require('./icon.svg');
const $ = window.jQuery!;

// eslint-disable-next-line camelcase
class GP_QR_Code {
	public $button: JQuery | undefined;
	public $popper: JQuery | undefined;
	public $scannerContainer: JQuery | undefined;
	public scanner: Html5QrcodeScanner | undefined;
	public popperInstance: PopperInstance | undefined;

	public formId: number;
	public fieldId: number;
	public $field: JQuery;

	public popperOpen?: boolean;

	constructor({ formId, fieldId }: { formId: number; fieldId: number }) {
		this.formId = formId;
		this.fieldId = fieldId;
		this.$field = $(`#field_${formId}_${fieldId}`);

		this.addButton();
		this.addPopper();
	}

	get $ginputContainer() {
		return this.$field.find('.ginput_container');
	}

	get $input() {
		return this.$field.find(
			'.ginput_container input:not(input[type="submit"], input[type="button"], input[type="hidden"]), .ginput_container textarea'
		);
	}

	addButton() {
		this.$ginputContainer.addClass('gpqr_ginput_container');

		this.$button = $(
			`<button class="gpqr-scanner-button" title="${window.GPQR.strings.openScanner}">${icon}</button>`
		);

		this.$field
			.find('.ginput_container input, .ginput_container textarea')
			.first()
			.after(this.$button);

		// Toggle popper when the button is clicked.
		this.$button.on('click', (event) => {
			if (!this.$popper?.is(':visible')) {
				this.showPopper();
			} else {
				this.closePopper();
			}

			event.preventDefault();
		});
	}

	addPopper() {
		this.$popper = $(
			`<div class="gpqr-scanner-popper gform-theme__disable" style="display: none;">
					<button class="gpqr-scanner-close" aria-label="${window.GPQR.strings.closeScanner}">&times;</button>
					<div id="scanner-container-${this.formId}-${this.fieldId}" class="scanner-container" />
				</div>`
		);

		this.$scannerContainer = this.$popper.find('.scanner-container');

		// Prevent buttons in popper from submitting the form
		this.$popper.on('click', 'button', (event) => {
			event.preventDefault();
		});

		// Hide popper when clicking the close button
		this.$popper.find('.gpqr-scanner-close').on('click', () => {
			this.closePopper();
		});

		this.$field.append(this.$popper);

		this.popperInstance = createPopper(this.$button![0], this.$popper![0], {
			placement: 'left',
			modifiers: [
				{
					name: 'offset',
					options: {
						offset: [0, 10],
					},
				},
			],
		});

		// Hide Popper if focus goes outside of field
		$(document).on('focus', ':input', (event) => {
			if (!this.popperOpen) {
				return;
			}

			if ($(event.target).closest(this.$field).length) {
				return;
			}

			if ($(event.target).closest(this.$scannerContainer!).length) {
				return;
			}

			this.closePopper();
		});
	}

	showPopper() {
		this.popperOpen = true;

		this.initScanner();
		this.$popper?.show();
		this.popperInstance?.update();
	}

	closePopper() {
		this.$popper?.hide();
		this.scanner?.clear();

		this.popperOpen = false;
	}

	initScanner() {
		// @todo Localize once supported https://github.com/mebjas/html5-qrcode/issues/132
		/**
		 * Filter initialize config for the scanner powered by html5-qrcode.
		 *
		 * @since 1.0-alpha-1
		 *
		 * @param object                      config Config used to initialize scanner.
		 * @param GP_QR_Code                  instance Current instance of the GP_QR_Code class.
		 * @param Html5QrcodeSupportedFormats supportedFormats Array of supported formats such as QR Code, PDF 417, etc. Provided as a way to pass in additional formats to the config.
		 * @param Html5QrcodeScanType         scanTypes Array of scan types (camera and file). Provided as a way to change the scan types in the config.
		 */
		this.scanner = new Html5QrcodeScanner(
			this.$scannerContainer![0].id,
			window.gform.applyFilters(
				'gpqr_scanner_config',
				{
					fps: 10,
					formatsToSupport: [Html5QrcodeSupportedFormats.QR_CODE],
					qrbox: { width: 300, height: 300 },
					videoConstraints: { facingMode: 'environment' },
					showZoomSliderIfSupported: false,
					supportedScanTypes: [
						Html5QrcodeScanType.SCAN_TYPE_CAMERA,
						Html5QrcodeScanType.SCAN_TYPE_FILE,
					],
				},
				this,
				Html5QrcodeSupportedFormats,
				Html5QrcodeScanType
			),
			false
		);

		this.scanner.render(this.onScanSuccess, this.onScanFailure);
	}

	destroyScanner() {
		this.scanner?.clear();
	}

	fillValue() {
		this.destroyScanner();
	}

	onScanSuccess = (decodedText: string, decodedResult: Html5QrcodeResult) => {
		this.closePopper();

		this.$input.focus().val(decodedText).trigger('change');

		/**
		 * Action after a code has been successfully scanned.
		 *
		 * @since 1.0-beta-1.2
		 *
		 * @param {string}            decodedText      Decoded text contents of the QR code.
		 * @param {Html5QrcodeResult} decodedResult    Detailed information about the decoded contents of the QR code.
		 * @param {GP_QR_Code}        gpqrCodeInstance Current instance of the GP QR Code class.
		 */
		window.gform.doAction(
			'gpqr_on_scan_success',
			decodedText,
			decodedResult,
			this
		);
	};

	onScanFailure = (errorMessage: string, error: Html5QrcodeError) => {
		// eslint-disable-next-line no-console
		console.debug('Unable to scan QR code.', { error });
	};
}

// eslint-disable-next-line camelcase
window.GP_QR_Code = GP_QR_Code;
