import {
	useState,
	useEffect,
	useRef
} from '@wordpress/element';
import {
	Flex,
	FlexItem,
	SelectControl,
	FlexBlock,
	TextControl,
} from '@wordpress/components';

import FontSelect from './FontSelect';

import ApuraRangeControl from './ApuraRangeControl';

import Accordion from './Accordion';

import TextTransform from './TextTransform';

import { __ } from '@wordpress/i18n';

import {
	ALL_DEVICE_DATA,
	TYPO_UNIT_SETTINGS,
	TYPO_UNIT_OPTIONS_DATA,
	FONT_SIZE_UNIT_OPTIONS_DATA,
	LETTER_SPACING_UNIT_OPTIONS_DATA,
	TYPO_LETTER_SPACING_UNIT_SETTINGS,
} from './Constants';

import { extractTypographyData, parseCssValue, createDeviceValue } from './utils';


export default function ApuraTypography({
	values,
	defaults,
	title,
	description,
	exclude = [],
	onChange,
}: {
	values: { [key: string]: string };
	defaults: { [key: string]: string };
	title: string;
	description?: string;
	exclude?: string[];
	onChange: (newValues: { [key: string]: string }) => void;
}) {
	const [typography, setTypography] = useState(values);
	// 初回マウント時に onChange が発火しないようにするフラグ.
	const isMounted = useRef(false);
	const [googleFonts, setGoogleFonts] = useState<GoogleFontItem[] | undefined | null>(apuraCustomizerControl?.googleFonts ?? []);

	useEffect(() => {
		wp.customize(apuraCustomizerControl?.googleFontSettingsId, (setting: any) => {
			setting.bind((newGoogleFonts: GoogleFontItem[]) => {
				setGoogleFonts(newGoogleFonts ?? []);
			});
		});
	}, []);

	useEffect(() => {
		// 新しいGoogleFontsを参照する。typographyの値は参照しない.
		// 新しいGoogleFontsのvariantsに..が含まれているかどうかでvariable fontかどうかを判定する。
		// もし新しいGoogleFontsのvariantsに..が含まれている場合、typographyのfont_weightが範囲外になっている可能性があるので、その場合はfont_weightを空にする。
		// ..がないということは一つの値のみが確定なので、その値を現在のfont_weightに設定する。
		if (typography.font_family) {
			const newFont = googleFonts?.find((font) => font.name === typography.font_family);
			if (newFont) {
				const isVariable = newFont.variants.some((variant) => variant.includes('..'));
				if (!isVariable) {
					// variable fontではない場合、font_weightを新しいフォントの最初のバリアントに設定する。
					setTypography((prev) => ({
						...prev,
						font_weight: '400',
					}));
				} else {
					// variable fontの場合、現在のfont_weightが新しいフォントの範囲外になっている可能性があるので、その場合はfont_weightを空にする。
					const [min, max] = newFont.variants.find((variant) => variant.includes('..'))?.split('..').map(Number) || [100, 900];
					const currentWeight = parseInt(typography.font_weight || '400', 10);
					if (currentWeight < min || currentWeight > max) {
						setTypography((prev) => ({
							...prev,
							font_weight: '',
						}));
					}
				}
			}
		}
	}, [googleFonts]);

	useEffect(() => {
		setTypography(values);
	}, [values]);

	useEffect(() => {
		// 初回マウント時はスキップし、typography が実際に変更された場合のみ onChange を呼ぶ.
		if ( ! isMounted.current ) {
			isMounted.current = true;
			return;
		}
		onChange(typography);
	}, [typography]);


	const prepareDeviceValues = (property: string): { deviceValues: RangeControlDeviceValue[]; deviceDefaults: RangeControlDeviceValue[] } => {
		const typographyData = extractTypographyData(typography, [property]);
		const defaultsData = extractTypographyData(defaults, [property]);

		const deviceValues: RangeControlDeviceValue[] = [];
		const deviceDefaults: RangeControlDeviceValue[] = [];


		ALL_DEVICE_DATA.forEach(({ device }) => {
			const parsedValue = parseCssValue(typographyData[property][device]) || 0;
			const parsedDefaults = parseCssValue(defaultsData[property][device]);

			const { value, default: defaultValue } = createDeviceValue(device, parsedValue, parsedDefaults, TYPO_UNIT_SETTINGS);
			deviceValues.push(value);
			deviceDefaults.push(defaultValue);
		});

		return { deviceValues, deviceDefaults };
	};

	const { deviceValues: fontSizeValues, deviceDefaults: fontSizeDefaults } = prepareDeviceValues('font_size');
	const { deviceValues: lineHeightValues, deviceDefaults: lineHeightDefaults } = prepareDeviceValues('line_height');
	const { deviceValues: letterSpacingValues, deviceDefaults: letterSpacingDefaults } = prepareDeviceValues('letter_spacing');

	const handleDevicesPropertyChange = (property: string, newValues: any) => {
		setTypography((prev) => ({
			...prev,
			[`${property}`]: newValues.desktop,
			[`tablet_${property}`]: newValues.tablet,
			[`mobile_${property}`]: newValues.mobile,
		}));
	};

	const handlePropertyChange = (property: string, newValue: string) => {
		setTypography((prev) => ({ ...prev, [`${property}`]: newValue }));
	};

	const handleFontFamilyChange = (selectedFont: string | null | undefined) => {
		// フォントを変更したらfont_weightを400にリセットする
		setTypography((prev) => (
			{ ...prev, font_family: selectedFont || '', font_weight: '400' }
		));
	};

	// 指定されたフォント名に対応するすべてのバリアントを取得
	const getAllVariantsForFont = (fontName: string | null | undefined): string[] => {
		if (!fontName) return [];
		return googleFonts?.find((data) => data.name === fontName)?.variants || [];
	};

	// フォントのバリアントからフォントウェイトのオプションを生成
	const getFontWeightOptions = (): { value: string; label: string }[] => {
		const defaultOptions = [
			{ value: '', label: __('Default', 'apura') },
			{ value: 'normal', label: __('Normal', 'apura') },
			{ value: 'bold', label: __('Bold', 'apura') },
			...Array.from({ length: 9 }, (_, i) => ({ value: `${i + 1}00`, label: `${i + 1}00` })),
		];

		if (!typography.font_family) {
			return defaultOptions;
		}

		const fontVariants = getAllVariantsForFont(typography.font_family);
		// System fonts don't have variants.
		if (!fontVariants.length) {
			return defaultOptions;
		}
		// italicなどの文字列を取り除いて数値部分のみを抽出し、重複を排除してソートする.
		const numericVariants = [...new Set(fontVariants.map((variant) => variant.replace('italic', '')))].sort((a, b) => a.localeCompare(b));

		// Sort variants.
		return numericVariants.map((variant) => ({ value: variant, label: variant }));
	};

	// Googleフォントにおいてvariable fontかどうかを判定. 100..900のようなバリアントが含まれているかどうかで判定
	// ただし、googleFontsがundefinedやnullの場合もあるので、その場合はfalseにする
	const currentFontVariants = getAllVariantsForFont(typography.font_family);
	const variableVariant = currentFontVariants.find((variant) => variant.includes('..'));
	const isVariableFont = !! variableVariant;
	const [variableFontWeightMin, variableFontWeightMax] = variableVariant?.split('..') ?? [100, 900];

	return (
		<div className="apura-typography-controls">
			<Accordion title={title} type="typography" isOpened={false}>
				<Flex direction="column" gap={4}>
					{description && (
						<FlexItem>
							<p className="description customize-control-description">{description}</p>
						</FlexItem>
					)}
					{!exclude.includes('font_family') && (
						<FlexItem>
							<FontSelect value={typography.font_family} googleFonts={googleFonts} onChange={handleFontFamilyChange} />
						</FlexItem>
					)}

					{(!exclude.includes('font_weight') || !exclude.includes('font_style')) && (
					<FlexItem>
						<Flex>
							{!exclude.includes('font_weight') && (
							<FlexBlock>
								{isVariableFont ? (
									<TextControl
										__next40pxDefaultSize
										__nextHasNoMarginBottom
										type="number"
										label={__('Font Weight', 'apura')}
										value={typography.font_weight || ''}
										onChange={(value) => handlePropertyChange('font_weight', value)}
										min={variableFontWeightMin}
										max={variableFontWeightMax}
										step={100}
									/>
								) : (
									<SelectControl
										__next40pxDefaultSize
										__nextHasNoMarginBottom
										label={__('Font Weight', 'apura')}
										value={typography.font_weight}
										options={getFontWeightOptions()}
										onChange={(value) => handlePropertyChange('font_weight', value)}
									/>
								)}
							</FlexBlock>
							)}
							{!exclude.includes('font_style') && (
							<FlexBlock>
								<SelectControl
									__next40pxDefaultSize
									__nextHasNoMarginBottom
									label={__('Font Style', 'apura')}
									value={typography.font_style as ("" | "normal" | "italic" | "oblique")}
									options={[
										{ value: '', label: __('Default', 'apura') },
										{ value: 'normal', label: __('Normal', 'apura') },
										{ value: 'italic', label: __('Italic', 'apura') },
										{ value: 'oblique', label: __('Oblique', 'apura') },
									]}
									onChange={(value) => handlePropertyChange('font_style', value)}
								/>
							</FlexBlock>
							)}
						</Flex>
					</FlexItem>
					)}

					{!exclude.includes('font_size') && (
					<FlexItem>
						<ApuraRangeControl
							values={fontSizeValues}
							unitOptions={FONT_SIZE_UNIT_OPTIONS_DATA}
							defaults={fontSizeDefaults}
							devices={ALL_DEVICE_DATA}
							unitData={TYPO_UNIT_SETTINGS}
							label={__('Font Size', 'apura')}
							onChange={(newValues) => handleDevicesPropertyChange('font_size', newValues)}
						/>
					</FlexItem>
					)}

					{!exclude.includes('line_height') && (
					<FlexItem>
						<ApuraRangeControl
							values={lineHeightValues}
							unitOptions={TYPO_UNIT_OPTIONS_DATA}
							defaults={lineHeightDefaults}
							devices={ALL_DEVICE_DATA}
							unitData={TYPO_UNIT_SETTINGS}
							label={__('Line Height', 'apura')}
							onChange={(newValues) => handleDevicesPropertyChange('line_height', newValues)}
						/>
					</FlexItem>
					)}

					{!exclude.includes('letter_spacing') && (
					<FlexItem>
						<ApuraRangeControl
							values={letterSpacingValues}
							unitOptions={LETTER_SPACING_UNIT_OPTIONS_DATA}
							defaults={letterSpacingDefaults}
							devices={ALL_DEVICE_DATA}
							unitData={TYPO_LETTER_SPACING_UNIT_SETTINGS}
							label={__('Letter Spacing', 'apura')}
							onChange={(newValues) => handleDevicesPropertyChange('letter_spacing', newValues)}
						/>
					</FlexItem>
					)}

					{!exclude.includes('text_transform') && (
					<FlexItem>
						<TextTransform
							value={typography?.text_transform || ''}
							items={[
								{ value: '', label: '-', title: __('Default', 'apura') },
								{ value: 'uppercase', label: 'AA', title: __('Uppercase', 'apura') },
								{ value: 'lowercase', label: 'aa', title: __('Lowercase', 'apura') },
								{ value: 'capitalize', label: 'Aa', title: __('Capitalize', 'apura') },
							]}
							onChange={(value) => handlePropertyChange('text_transform', value)}
						/>
					</FlexItem>
					)}
				</Flex>
			</Accordion>
		</div>
	);
}
