import * as React from 'react'

import {
	useCallback,
	useMemo,
	useState,
} from '@wordpress/element'

import {
	Button,
	Modal,
	__experimentalHStack as HStack,
} from '@wordpress/components'

import {
	diffLines,
	formatLines,
// @ts-expect-error
} from 'unidiff'

import {
	parseDiff,
	Diff,
	Hunk,
	type ChangeEventArgs,
} from 'react-diff-view'

import './style.scss'

export interface DiffViewProps {
	aValue?: string | string[]
	bValue?: string | string[]
	aTitle?: string
	bTitle?: string
	onChange?: (args: {
		side: 'old' | 'new'
		type: 'delete' | 'insert'
		lineNumber: number
		content: string
		/** Whether the value should be replaced (is insert) or added. */
		isInsert: boolean
		/** Whether the value should be replaced (is delete) or added. */
		isDelete: boolean
	}) => void
}

interface SubComponents {
	Modal: typeof DiffViewModal
}

const DiffView: React.FC<DiffViewProps> & SubComponents = ({
	aValue = '',
	bValue = '',
	aTitle = '',
	bTitle = '',
	onChange,
	...props
}) => {

	const lines = diffLines(aValue, bValue)
	const diffText = formatLines(lines, {context: 3})
	const [diff] = parseDiff(diffText, {nearbySequences: 'zip'})

    const onGutterClick = useCallback(({side, change}: ChangeEventArgs) => {

		if(!onChange || !side || !change) {
			return
		}

		const {
			type,
			// @ts-expect-error
			lineNumber = -1,
			content,
			// @ts-expect-error
			isInsert = false,
			// @ts-expect-error
			isDelete = false,
		} = change

		if(type !== 'delete' && type !== 'insert') {
			return
		}

		onChange({
			side,
			type,
			lineNumber,
			content,
			isInsert,
			isDelete,
		})
	}, [onChange])

    const diffProps = useMemo(
        () => {
            return {
                gutterEvents: {
					onClick: onGutterClick,
				},
            }
        },
        [onGutterClick]
    )

	return (
		<div className='ska__diff-view'>
			{aTitle && bTitle && (
				<HStack justify='space-between'>
					<h2>{aTitle}</h2>
					<h2>{bTitle}</h2>
				</HStack>
			)}
			<Diff
				viewType='split'
				diffType='modify'
				hunks={diff.hunks}
				{...diffProps}
			>
				{hunks => hunks.map(hunk => <Hunk key={hunk.content} hunk={hunk} />)}
			</Diff>
		</div>
	)
}

export interface DiffViewModalProps extends DiffViewProps {
	buttonProps?: any
	modalProps?: any
}

const DiffViewModal: React.FC<DiffViewModalProps> = ({modalProps = {}, buttonProps = {}, ...props}) => {

	const [isOpen, setIsOpen] = useState(false)

	const {
		aValue = '',
		bValue = '',
		aTitle = '',
		bTitle = '',
	} = props

	const {
		title = 'Diff',
	} = modalProps

	const modalTitle = aTitle && bTitle ? `${title} (${aTitle} - ${bTitle})` : title

	return <>
		<Button
			size='small'
			children='View diff'
			onClick={() => setIsOpen(true)}
			{...buttonProps}
			disabled={isOpen || aValue === bValue || buttonProps?.disabled}
		/>
		{isOpen && (
			<Modal
				size='fill'
				{...modalProps}
				title={modalTitle}
				onRequestClose={() => setIsOpen(false)}
			>
				<DiffView {...props} />
			</Modal>
		)}
	</>
}

DiffView.Modal = DiffViewModal

export default DiffView
