import * as React from 'react'
import {sprintf} from '@wordpress/i18n'

import {
	useState,
	createContext,
	useContext,
} from '@wordpress/element'

import {
	Modal,
	__experimentalVStack as VStack,
	ComboboxControl,
} from '@wordpress/components'

import type {
	TextControlProps,
} from '@wordpress/components/build-types/text-control/types'

import {
	Button,
	ButtonGroup,
	ArrayEditor,
	ArrayEditorProps,
} from '@ska/components'

import GoogleFont, {
	type GoogleFontControlsProps,
} from './GoogleFont'

import VariableGoogleFont, {
	type VariableGoogleFontControlsProps,
} from './VariableGoogleFont'

import './style.scss'

export {
	GoogleFont,
	GoogleFontControlsProps,
	VariableGoogleFont,
	VariableGoogleFontControlsProps,
}

export interface FontControlStrings {
	editButtonText: string
	modalTitle: string
	localFontsLabel: string
	noExternalFontMessage: string
	unknownFontSourceMessage: string
	externalFontSourceLabel: string
	selectFontLabel: string
	variantsLabel: string
	minWghtLabel: string
	maxWghtLabel: string
}
export const FONT_CONTROL_STRINGS = {
	editButtonText: 'Edit',
	modalTitle: 'Edit font',
	localFontsLabel: 'Local fonts',
	noExternalFontMessage: 'No external font',
	unknownFontSourceMessage: 'Unknown font source: %s',
	externalFontSourceLabel: 'External font source',
	selectFontLabel: 'Select a font',
	variantsLabel: 'Variants',
	minWghtLabel: 'Minimum font-weight',
	maxWghtLabel: 'Maximum font-weight',
}
export const FontControlStringsContext = createContext<FontControlStrings>(FONT_CONTROL_STRINGS)

export const FontSourcesContext = createContext<FontSource[]>([])

export interface FontControlsProps {
	font: any
	value: Record<string, any>
	onChange: (nextValue: FontControlsProps['value']) => void
}

export interface FontSource {
	id: string
	label: string
	fonts: any[]
	renderControls?: React.FC<FontControlsProps>
}

export interface FontValue {
	source?: FontSource['id']
	font?: string
}

interface FontPickerProps {
	value: FontValue
	onChange: (nextValue: FontValue) => void
}

const FontPicker: React.FC<FontSource & FontPickerProps> = ({
	id,
	fonts,
	renderControls: Controls,
	value,
	onChange,
}) => {

	const {
		selectFontLabel,
	} = useContext(FontControlStringsContext)

	const {
		font = '',
	} = value

	const options = fonts.map(({name}) => ({
		label: name,
		value: name,
	}))

	const currentFont = font && fonts.find(f => f.name === font)

	return <>
		<ComboboxControl
			key={id}
			label={selectFontLabel}
			value={font}
			options={options}
			onChange={nextFont => {
				onChange({
					source: value.source,
					font: nextFont || '',
				})
			}}
			__next40pxDefaultSize
			__nextHasNoMarginBottom
		/>
		{currentFont && Controls && (
			<Controls
				key={font}
				font={currentFont}
				value={value}
				onChange={onChange}
			/>
		)}
	</>
}

interface ExternalFontPickerProps {
	value: FontValue
	onChange: (nextValue: FontValue) => void
}

const ExternalFontPicker: React.FC<ExternalFontPickerProps> = ({
	value,
	onChange,
}) => {

	const {
		noExternalFontMessage,
		unknownFontSourceMessage,
		externalFontSourceLabel,
	} = useContext(FontControlStringsContext)

	const sources = useContext(FontSourcesContext)

	const {
		source = '',
	} = value

	const selectedFontSource = sources.find(({id}) => id === source)

	const options = [{
		label: noExternalFontMessage,
		value: 'default',
	}].concat(
		sources.map(({id, label}) => ({
			label,
			value: id,
		}))
	).concat([
		...(source && !selectedFontSource ? [{
			label: sprintf(unknownFontSourceMessage, source),
			value: source,
		}] : []),
	])

	return <>
		<ButtonGroup.Radio
			pills
			isSmall
			label={externalFontSourceLabel}
			value={source || 'default'}
			options={options}
			onChange={(nextSource: string) => {
				if(nextSource) {
					onChange({source: nextSource === 'default' ? '' : nextSource})
				}
			}}
		/>
		{selectedFontSource && (
			<FontPicker
				key={selectedFontSource.id}
				{...selectedFontSource}
				value={value}
				onChange={onChange}
			/>
		)}
	</>
}

export interface FontControlProps extends Omit<TextControlProps, 'value'> {
	value: string
	disabled?: boolean
	arrayEditorProps?: Partial<ArrayEditorProps>
}

const FontControl: React.FC<FontControlProps> = ({
	value = '',
	onChange,
	disabled = false,
	arrayEditorProps = {},
}) => {

	const {
		editButtonText,
		modalTitle,
		localFontsLabel,
	} = useContext(FontControlStringsContext)

	const [isOpen, setIsOpen] = useState(false)
	const [externalFontJson, systemFonts] = (value.indexOf('[&]') === -1 ? `{}[&]${value}` : value).split('[&]')
	const externalFont = JSON.parse(externalFontJson)
	const fonts = systemFonts.split(',').map(v => v.trim())

	return <>
		<Button
			className='ska-components-font-control__button'
			children={editButtonText}
			variant='secondary'
			onClick={() => setIsOpen(!isOpen)}
			disabled={disabled}
		/>
		{isOpen && (
			<Modal
				title={modalTitle}
				onRequestClose={() => setIsOpen(false)}
				style={{width: '100%', maxWidth: 512}}
			>
				<VStack spacing={4}>
					<ExternalFontPicker
						value={externalFont}
						onChange={nextValue => {
							const {source = ''} = nextValue
							onChange(source ? [JSON.stringify(nextValue), systemFonts].join('[&]') : systemFonts)
						}}
					/>
					<ArrayEditor
						id='fonts'
						label={localFontsLabel}
						value={fonts}
						onChange={nextValue => {
							const nextLocalFonts = nextValue.map(v => v.replaceAll(',', '')).join(',')
							onChange(externalFontJson === '{}' ? nextLocalFonts : [externalFontJson, nextLocalFonts].join('[&]'))
						}}
						{...arrayEditorProps}
					/>
				</VStack>
			</Modal>
		)}
	</>
}

export default FontControl
