import { json } from '@codemirror/lang-json'
import { yaml } from '@codemirror/lang-yaml'
import { useFetcher } from '@remix-run/react'
import { githubDark, githubLight } from '@uiw/codemirror-theme-github'
import CodeMirror from '@uiw/react-codemirror'
import clsx from 'clsx'
import { useEffect, useMemo, useState } from 'react'
import CodeMirrorMerge from 'react-codemirror-merge'
import { toast } from 'react-hot-toast/headless'

import Button from '~/components/Button'
import Spinner from '~/components/Spinner'

import Fallback from './fallback'

type EditorProperties = {
	readonly acl: string;
	readonly setAcl: (acl: string) => void;
	readonly mode: 'edit' | 'diff';

	readonly data: {
		hasAclWrite: boolean;
		currentAcl: string;
		aclType: string;
	};
}

export default function Editor({ data, acl, setAcl, mode }: EditorProperties) {
	const [light, setLight] = useState(false)
	const [loading, setLoading] = useState(true)

	const fetcher = useFetcher()
	const aclType = useMemo(() => data.aclType === 'json' ? json() : yaml(), [data.aclType])

	useEffect(() => {
		const theme = window.matchMedia('(prefers-color-scheme: light)')
		setLight(theme.matches)

		theme.addEventListener('change', theme => {
			setLight(theme.matches)
		})

		// Prevents the FOUC
		setLoading(false)
	}, [])

	return (
		<>
			<div className={clsx(
				'border border-gray-200 dark:border-gray-700',
				'rounded-b-lg rounded-tr-lg mb-2 overflow-hidden'
			)}
			>
				{loading ? (
					<Fallback acl={acl} where='client'/>
				) : (
					mode === 'edit' ? (
						<CodeMirror
							value={acl}
							className='h-editor text-sm'
							theme={light ? githubLight : githubDark}
							extensions={[aclType]}
							readOnly={!data.hasAclWrite}
							onChange={value => {
								setAcl(value)
							}}
						/>
					) : (
						<div
							className='overflow-y-scroll'
							style={{ height: 'calc(100vh - 20rem)' }}
						>
							<CodeMirrorMerge
								theme={light ? githubLight : githubDark}
								orientation='a-b'
							>
								<CodeMirrorMerge.Original
									readOnly
									value={data.currentAcl}
									extensions={[aclType]}
								/>
								<CodeMirrorMerge.Modified
									readOnly
									value={acl}
									extensions={[aclType]}
								/>
							</CodeMirrorMerge>
						</div>
					)
				)}
			</div>

			<Button
				variant='emphasized'
				className='text-sm w-fit mr-2'
				onClick={() => {
					fetcher.submit({
						acl
					}, {
						method: 'PATCH',
						encType: 'application/json'
					})

					toast('Updated tailnet ACL policy')
				}}
			>
				{fetcher.state === 'idle' ? undefined : (
					<Spinner className='w-3 h-3'/>
				)}
				Save
			</Button>
			<Button
				variant='emphasized'
				className={clsx(
					'text-sm w-fit bg-gray-100 dark:bg-transparent',
					'border border-gray-200 dark:border-gray-700'
				)}
				onClick={() => {
					setAcl(data.currentAcl)
				}}
			>
				Discard Changes
			</Button>
		</>
	)
}
