import {
	ArrowShapeArrowheadEndStyle,
	ArrowShapeArrowheadStartStyle,
	DefaultColorStyle,
	DefaultDashStyle,
	DefaultFillStyle,
	DefaultFontStyle,
	DefaultHorizontalAlignStyle,
	DefaultSizeStyle,
	DefaultVerticalAlignStyle,
	GeoShapeGeoStyle,
	LineShapeSplineStyle,
	ReadonlySharedStyleMap,
	StyleProp,
	TLArrowShapeArrowheadStyle,
	minBy,
	useEditor,
	useValue,
} from '@tldraw/editor'
import React from 'react'
import { STYLES } from '../../../styles'
import { useUiEvents } from '../../context/events'
import { useRelevantStyles } from '../../hooks/useRelevantStyles'
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon'
import { TldrawUiButtonPicker } from '../primitives/TldrawUiButtonPicker'
import { TldrawUiSlider } from '../primitives/TldrawUiSlider'
import { DoubleDropdownPicker } from './DoubleDropdownPicker'
import { DropdownPicker } from './DropdownPicker'

/** @public */
export type TLUiStylePanelContentProps = {
	styles: ReturnType<typeof useRelevantStyles>
}

/** @public */
export function DefaultStylePanelContent({ styles }: TLUiStylePanelContentProps) {
	if (!styles) return null

	const geo = styles.get(GeoShapeGeoStyle)
	const arrowheadEnd = styles.get(ArrowShapeArrowheadEndStyle)
	const arrowheadStart = styles.get(ArrowShapeArrowheadStartStyle)
	const spline = styles.get(LineShapeSplineStyle)
	const font = styles.get(DefaultFontStyle)

	const hideGeo = geo === undefined
	const hideArrowHeads = arrowheadEnd === undefined && arrowheadStart === undefined
	const hideSpline = spline === undefined
	const hideText = font === undefined

	return (
		<>
			<CommonStylePickerSet styles={styles} />
			{!hideText && <TextStylePickerSet styles={styles} />}
			{!(hideGeo && hideArrowHeads && hideSpline) && (
				<div className="tlui-style-panel__section" aria-label="style panel styles">
					<GeoStylePickerSet styles={styles} />
					<ArrowheadStylePickerSet styles={styles} />
					<SplineStylePickerSet styles={styles} />
				</div>
			)}
		</>
	)
}

function useStyleChangeCallback() {
	const editor = useEditor()
	const trackEvent = useUiEvents()

	return React.useMemo(
		() =>
			function handleStyleChange<T>(style: StyleProp<T>, value: T, squashing: boolean) {
				editor.batch(() => {
					if (editor.isIn('select')) {
						editor.setStyleForSelectedShapes(style, value, { squashing })
					}
					editor.setStyleForNextShapes(style, value, { squashing })
					editor.updateInstanceState({ isChangingStyle: true }, { ephemeral: true })
				})

				trackEvent('set-style', { source: 'style-panel', id: style.id, value: value as string })
			},
		[editor, trackEvent]
	)
}

/** @public */
export function CommonStylePickerSet({ styles }: { styles: ReadonlySharedStyleMap }) {
	const msg = useTranslation()

	const handleValueChange = useStyleChangeCallback()

	const color = styles.get(DefaultColorStyle)
	const fill = styles.get(DefaultFillStyle)
	const dash = styles.get(DefaultDashStyle)
	const size = styles.get(DefaultSizeStyle)

	const showPickers = fill !== undefined || dash !== undefined || size !== undefined

	return (
		<>
			<div
				tabIndex={-1}
				className="tlui-style-panel__section__common"
				aria-label="style panel styles"
				data-testid="style.panel"
			>
				{color === undefined ? null : (
					<TldrawUiButtonPicker
						title={msg('style-panel.color')}
						uiType="color"
						style={DefaultColorStyle}
						items={STYLES.color}
						value={color}
						onValueChange={handleValueChange}
					/>
				)}
				<OpacitySlider />
			</div>
			{showPickers && (
				<div className="tlui-style-panel__section" aria-label="style panel styles">
					{fill === undefined ? null : (
						<TldrawUiButtonPicker
							title={msg('style-panel.fill')}
							uiType="fill"
							style={DefaultFillStyle}
							items={STYLES.fill}
							value={fill}
							onValueChange={handleValueChange}
						/>
					)}
					{dash === undefined ? null : (
						<TldrawUiButtonPicker
							title={msg('style-panel.dash')}
							uiType="dash"
							style={DefaultDashStyle}
							items={STYLES.dash}
							value={dash}
							onValueChange={handleValueChange}
						/>
					)}
					{size === undefined ? null : (
						<TldrawUiButtonPicker
							title={msg('style-panel.size')}
							uiType="size"
							style={DefaultSizeStyle}
							items={STYLES.size}
							value={size}
							onValueChange={handleValueChange}
						/>
					)}
				</div>
			)}
		</>
	)
}

/** @public */
export function TextStylePickerSet({ styles }: { styles: ReadonlySharedStyleMap }) {
	const msg = useTranslation()
	const handleValueChange = useStyleChangeCallback()

	const font = styles.get(DefaultFontStyle)
	const align = styles.get(DefaultHorizontalAlignStyle)
	const verticalAlign = styles.get(DefaultVerticalAlignStyle)
	if (font === undefined && align === undefined) {
		return null
	}

	return (
		<div className="tlui-style-panel__section" aria-label="style panel text">
			{font === undefined ? null : (
				<TldrawUiButtonPicker
					title={msg('style-panel.font')}
					uiType="font"
					style={DefaultFontStyle}
					items={STYLES.font}
					value={font}
					onValueChange={handleValueChange}
				/>
			)}

			{align === undefined ? null : (
				<div className="tlui-style-panel__row">
					<TldrawUiButtonPicker
						title={msg('style-panel.align')}
						uiType="align"
						style={DefaultHorizontalAlignStyle}
						items={STYLES.horizontalAlign}
						value={align}
						onValueChange={handleValueChange}
					/>
					<div className="tlui-style-panel__row__extra-button">
						{verticalAlign === undefined ? (
							<TldrawUiButton
								type="icon"
								title={msg('style-panel.vertical-align')}
								data-testid="vertical-align"
								disabled
							>
								<TldrawUiButtonIcon icon="vertical-align-center" />
							</TldrawUiButton>
						) : (
							<DropdownPicker
								type="icon"
								id="geo-vertical-alignment"
								uiType="verticalAlign"
								style={DefaultVerticalAlignStyle}
								items={STYLES.verticalAlign}
								value={verticalAlign}
								onValueChange={handleValueChange}
							/>
						)}
					</div>
				</div>
			)}
		</div>
	)
}
/** @public */
export function GeoStylePickerSet({ styles }: { styles: ReadonlySharedStyleMap }) {
	const handleValueChange = useStyleChangeCallback()

	const geo = styles.get(GeoShapeGeoStyle)
	if (geo === undefined) {
		return null
	}

	return (
		<DropdownPicker
			id="geo"
			type="menu"
			label={'style-panel.geo'}
			uiType="geo"
			style={GeoShapeGeoStyle}
			items={STYLES.geo}
			value={geo}
			onValueChange={handleValueChange}
		/>
	)
}
/** @public */
export function SplineStylePickerSet({ styles }: { styles: ReadonlySharedStyleMap }) {
	const handleValueChange = useStyleChangeCallback()

	const spline = styles.get(LineShapeSplineStyle)
	if (spline === undefined) {
		return null
	}

	return (
		<DropdownPicker
			id="spline"
			type="menu"
			label={'style-panel.spline'}
			uiType="spline"
			style={LineShapeSplineStyle}
			items={STYLES.spline}
			value={spline}
			onValueChange={handleValueChange}
		/>
	)
}

/** @public */
export function ArrowheadStylePickerSet({ styles }: { styles: ReadonlySharedStyleMap }) {
	const handleValueChange = useStyleChangeCallback()

	const arrowheadEnd = styles.get(ArrowShapeArrowheadEndStyle)
	const arrowheadStart = styles.get(ArrowShapeArrowheadStartStyle)
	if (!arrowheadEnd || !arrowheadStart) {
		return null
	}

	return (
		<DoubleDropdownPicker<TLArrowShapeArrowheadStyle>
			label={'style-panel.arrowheads'}
			uiTypeA="arrowheadStart"
			styleA={ArrowShapeArrowheadStartStyle}
			itemsA={STYLES.arrowheadStart}
			valueA={arrowheadStart}
			uiTypeB="arrowheadEnd"
			styleB={ArrowShapeArrowheadEndStyle}
			itemsB={STYLES.arrowheadEnd}
			valueB={arrowheadEnd}
			onValueChange={handleValueChange}
			labelA="style-panel.arrowhead-start"
			labelB="style-panel.arrowhead-end"
		/>
	)
}

const tldrawSupportedOpacities = [0.1, 0.25, 0.5, 0.75, 1] as const
/** @public */
export function OpacitySlider() {
	const editor = useEditor()
	const opacity = useValue('opacity', () => editor.getSharedOpacity(), [editor])
	const trackEvent = useUiEvents()
	const msg = useTranslation()

	const handleOpacityValueChange = React.useCallback(
		(value: number, squashing: boolean) => {
			const item = tldrawSupportedOpacities[value]
			editor.batch(() => {
				if (editor.isIn('select')) {
					editor.setOpacityForSelectedShapes(item, { squashing })
				}
				editor.setOpacityForNextShapes(item, { squashing })
				editor.updateInstanceState({ isChangingStyle: true }, { ephemeral: true })
			})

			trackEvent('set-style', { source: 'style-panel', id: 'opacity', value })
		},
		[editor, trackEvent]
	)

	if (opacity === undefined) return null

	const opacityIndex =
		opacity.type === 'mixed'
			? -1
			: tldrawSupportedOpacities.indexOf(
					minBy(tldrawSupportedOpacities, (supportedOpacity) =>
						Math.abs(supportedOpacity - opacity.value)
					)!
				)

	return (
		<TldrawUiSlider
			data-testid="style.opacity"
			value={opacityIndex >= 0 ? opacityIndex : tldrawSupportedOpacities.length - 1}
			label={opacity.type === 'mixed' ? 'style-panel.mixed' : `opacity-style.${opacity.value}`}
			onValueChange={handleOpacityValueChange}
			steps={tldrawSupportedOpacities.length - 1}
			title={msg('style-panel.opacity')}
		/>
	)
}
