import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import renderHtmlAttributes from "../../utils/renderHtmlAttributes";
import useFormSubmitSuccess from "../../hooks/useFormSubmitSuccess";
import type {PointGroup} from "signature_pad";
import SignaturePad from "signature_pad";
// @ts-ignore
import trimCanvas from 'trim-canvas'
import type {FreemiusProps} from "../../types/freemius";
import UpgradeOverlay from "../../components/UpgradeOverlay";

type FieldProps = {
	itemId: string
	formId: string
	attrs: Record<string, string | string[]>
	showPlaceholder: boolean
	showColorsList: boolean
	clearCanvas: boolean
	includeBgColor: boolean
	trimCanvas: boolean
	showUndoButton: boolean
	showRedoButton: boolean
	placeholderIcon: string
	colorsListLabel: string
	undoButtonText: string
	redoButtonText: string
	clearButtonText: string
	backgroundColor: string
	sendFiles: 'link' | 'attach' | 'both'
	penColor: string
	fileName: string
	colorsList: { title: string; color: string }[]
	custom_id: string
	buttonAnimation: string
} & FreemiusProps

const Field = (props: FieldProps) => {

	const inputRef = useRef<HTMLInputElement | null>(null);
	const inputHiddenRef = useRef<HTMLInputElement | null>(null);
	const canvasRef = useRef<HTMLCanvasElement | null>(null);
	const padRef = useRef<SignaturePad | null>(null);
	const attrs = useMemo(() => renderHtmlAttributes(props.attrs), [props.attrs]);
	const [undoData, setUndoData] = useState<PointGroup[]>([]);
	const [isBeginStroke, setBeginStroke] = useState(false);
	const [isEmpty, setIsEmpty] = useState(true);

	// Reset canvas
	const reset: () => void = useCallback(() => {
		const signaturePad = padRef.current as SignaturePad;
		signaturePad.clear();
		setIsEmpty(true);
		setBeginStroke(false);
		(inputRef.current as HTMLInputElement).value = '';
		(inputHiddenRef.current as HTMLInputElement).value = '';
	}, []);

	// Reset after form submit
	useFormSubmitSuccess(inputRef, () => {
		reset();
	}, [reset]);

	const placeholderIcon = useMemo(() => decodeURIComponent(props.placeholderIcon), [props.placeholderIcon]);

	// Get data url with background color
	const getTempCanvasDataUrl: (bgColor: string) => string = useCallback((bgColor: string) => {
		const canvas = canvasRef.current as HTMLCanvasElement;
		const signaturePad = padRef.current as SignaturePad;
		const tempCanvas = document.createElement('canvas');
		tempCanvas.width = canvas.width;
		tempCanvas.height = canvas.height;
		const ratio = Math.max(window.devicePixelRatio || 1, 1);
		tempCanvas.getContext('2d')?.scale(ratio, ratio);
		const tempSignaturePad = new SignaturePad(tempCanvas, {
			penColor: signaturePad.penColor,
			backgroundColor: props.includeBgColor ? bgColor : 'rgba(0,0,0,0)',
			minWidth: signaturePad.minWidth,
			maxWidth: signaturePad.maxWidth,
			throttle: signaturePad.throttle,
			dotSize: signaturePad.dotSize,
			minDistance: signaturePad.minDistance,
			velocityFilterWeight: signaturePad.velocityFilterWeight
		});
		tempSignaturePad.fromData(signaturePad.toData());
		if (props.trimCanvas) {
			trimCanvas(tempCanvas);
		}
		const url = tempSignaturePad.toDataURL();
		tempSignaturePad.off();
		tempCanvas.remove();
		return url;
	}, [props.includeBgColor, props.trimCanvas]);

	// Create image
	const createImage: () => Promise<void> = useCallback(async () => {
		const dataUrl = getTempCanvasDataUrl(props.backgroundColor);
		const imgResponse = await fetch(dataUrl);
		const imgBlob = await imgResponse.blob();
		// Create a new file
		const fileName = `${props.fileName}.png`;
		const file = new File([imgBlob], fileName);
		// Add file
		const fileList = new DataTransfer();
		fileList.items.add(file);
		(inputRef.current as HTMLInputElement).files = fileList.files;
		(inputHiddenRef.current as HTMLInputElement).value = fileName;
	}, [getTempCanvasDataUrl, props.backgroundColor, props.fileName]);

	const isUndoData = useMemo(() => undoData.length > 0, [undoData.length]);

	useEffect(() => {
		const canvas = canvasRef.current as HTMLCanvasElement;
		if (!canvas) {
			return;
		}
		const signaturePad = new SignaturePad(canvas, {
			penColor: props.penColor,
			backgroundColor: 'rgba(0,0,0,0)', // Transparent
			minWidth: 0.5,
			maxWidth: 2.5,
			throttle: 16,
			dotSize: 0,
			minDistance: 0,
			velocityFilterWeight: 0.7,
		});

		padRef.current = signaturePad;

		/**
		 * Adjust canvas coordinate space taking into account pixel ratio,
		 * to make it look crisp on mobile devices.
		 * This also causes canvas to be cleared.
		 */
		function resizeCanvas() {
			// When zoomed out to less than 100%, for some very strange reason,
			// some browsers report devicePixelRatio as less than 1
			// and only part of the canvas is cleared then.
			const ratio = Math.max(window.devicePixelRatio || 1, 1);
			// This part causes the canvas to be cleared
			canvas.width = canvas.offsetWidth * ratio;
			canvas.height = canvas.offsetHeight * ratio;
			canvas.getContext('2d')?.scale(ratio, ratio);
			// signaturePad.clear();
			// If you want to keep the drawing on resize instead of clearing it you can reset the data.
			signaturePad.fromData(signaturePad.toData());
		}

		// Triggered before stroke begins
		function beginStroke() {
			setBeginStroke(true);
		}

		// Triggered after stroke ends
		const endStroke = async () => {
			// Clear undoData when new data is added
			setUndoData([]);
			setIsEmpty(signaturePad.isEmpty());
			await createImage();
		};

		// Bind events
		window.addEventListener('resize', resizeCanvas, false);
		signaturePad.addEventListener("beginStroke", beginStroke, false);
		signaturePad.addEventListener('endStroke', endStroke, false);

		resizeCanvas();

		return () => {
			window.removeEventListener('resize', resizeCanvas, false);
			signaturePad.removeEventListener('beginStroke', endStroke, false);
			signaturePad.removeEventListener('endStroke', endStroke, false);
			signaturePad?.off();
			signaturePad?.clear();
		}
	}, [createImage, props.penColor]);

	useEffect(() => {
		if (isEmpty) {
			setBeginStroke(false);
		} else {
			setBeginStroke(true);
		}
	}, [isEmpty]);

	// Undo button
	async function handleUndo(e: React.MouseEvent<HTMLButtonElement>) {
		e.preventDefault();
		if (!props.freemius.can_use_premium_code) {
			return;
		}
		const signaturePad = padRef.current as SignaturePad;
		const data = signaturePad.toData();
		if (!(data && data.length > 0)) {
			return;
		}
		// Remove the last dot or line
		const removed = data.pop();
		if (removed) {
			setUndoData(prevState => ([
				...prevState,
				removed
			]));
		}
		signaturePad.fromData(data);
		setIsEmpty(signaturePad.isEmpty());
		// Reset if pad is empty
		if (signaturePad.isEmpty()) {
			reset();
		}
	}

	// Redo Button
	async function handleRedo(e: React.MouseEvent<HTMLButtonElement>) {
		e.preventDefault();
		if (!props.freemius.can_use_premium_code) {
			return;
		}
		if (undoData.length <= 0) {
			return;
		}
		const signaturePad = padRef.current as SignaturePad;
		const data = signaturePad.toData();
		data.push(undoData.pop() as PointGroup);
		signaturePad.fromData(data);
		if (undoData.length <= 0) {
			setUndoData([]);
		}
		setIsEmpty(signaturePad.isEmpty());
		await createImage();
	}

	// Clear button
	function handleClear(e: React.MouseEvent<HTMLButtonElement>) {
		e.preventDefault();
		if (!props.freemius.can_use_premium_code) {
			return;
		}
		setUndoData([]);
		reset();
	}

	// Select color
	function handleColor(e: React.ChangeEvent<HTMLInputElement>) {
		if (!props.freemius.can_use_premium_code) {
			return;
		}
		const value = e.target.value.trim().toLowerCase();
		const signaturePad = padRef.current as SignaturePad;
		signaturePad.penColor = value;
		// Clear canvas
		if (props.clearCanvas) {
			setUndoData([]);
			reset();
		}
	}

	return (
		<>
			{!props.freemius.can_use_premium_code && <UpgradeOverlay url={props.freemius.get_upgrade_url}/>}
			<div className="hulk-pad-wrap" data-begin={isBeginStroke}>
				<canvas
					ref={canvasRef}
					className="hulk-canvas"
					width="400"
					height="200"
				></canvas>
				{props.showPlaceholder && (
					<div
						className="hulk-placeholder"
						dangerouslySetInnerHTML={{__html: placeholderIcon}}
					/>
				)}
			</div>
			<div className="hulk-pad-footer" data-select-color={props.showColorsList}>
				{(props.showColorsList && props.colorsList.length > 0) && (
					<div className={'hulk-colors-list'}>
						<label
							className={'hulk-colors-list-label'}
							htmlFor={`hulk-colors-list-${props.itemId}-0`}
						>{props.colorsListLabel}</label>
						{props.colorsList.map(({title, color}, index) => (
							<input
								key={`hulk-colors-list-${props.itemId}-${index}`}
								id={`hulk-colors-list-${props.itemId}-${index}`}
								className={'hulk-colors-list-color'}
								style={{backgroundColor: color, borderColor: color}}
								name={`hulk-ffef-colors-list-color-${props.itemId}`}
								defaultValue={color}
								defaultChecked={index <= 0}
								title={title}
								aria-label={title}
								type="radio"
								onChange={handleColor}
							/>
						))}
					</div>
				)}
				<div className="hulk-buttons">
					{props.showUndoButton && (
						<button
							type="button"
							className={`elementor-button elementor-animation-${props.buttonAnimation}`}
							onClick={handleUndo}
							disabled={isEmpty}
						>{props.undoButtonText}</button>
					)}
					{(props.showUndoButton && props.showRedoButton) && (
						<button
							type="button"
							className={`elementor-button elementor-animation-${props.buttonAnimation}`}
							disabled={!isUndoData}
							onClick={handleRedo}
						>{props.redoButtonText}</button>
					)}
					{props.clearButtonText && (
						<button
							type="button"
							className={`elementor-button elementor-animation-${props.buttonAnimation}`}
							disabled={isEmpty && !isUndoData}
							onClick={handleClear}
						>{props.clearButtonText}</button>
					)}
				</div>
			</div>
			<input ref={inputHiddenRef} type="hidden" name={attrs.name}/>
			<input
				ref={inputRef}
				{...attrs}
				id={`${attrs.id}-signature`}
				type="file"
				accept="image/*"
			/>
			<div id={attrs.id}/>
		</>
	);
};

export default Field;
