/**
 * Package settings form: owns state, validation (Zod), and errors.
 * Notifies page via onChange(formId, value) and onInteraction().
 */
import { __ } from '@wordpress/i18n';
import { memo, useState, useEffect, useCallback, useRef } from '@wordpress/element';
import { Card, CardBody, Flex, Notice } from '@wordpress/components';
import { Spinner } from '@woocommerce/components';
import { SettingsField } from './settings-field';
import { formatDisplayNumber, getDimensionBounds } from '../../shared/utils';
import type {
	PackagePreset,
	NewPackageForm,
	PackagesFormValue,
	UpdateSettingsPayload,
	UpdateSettingsResponse,
} from '../../types/settings';
import { packagePresetArraySchema } from './utils/schemas';
import type { ZodError } from 'zod';

export const PACKAGES_FORM_ID = 'packages' as const;

function buildDimensionRangeMessage(dimensionUnit: string, min: number, max: number): string {
	return __(
		`Dimension must be between ${formatDisplayNumber(min)} and ${formatDisplayNumber(max)} ${dimensionUnit} (1-150cm equivalent).`,
		'parcel2go-shipping'
	);
}

function formatPackageLabel(pkg: PackagePreset, dimensionUnit: string): string {
	return `${pkg.name} - ${formatDisplayNumber(pkg.length)}x${formatDisplayNumber(pkg.width)}x${formatDisplayNumber(pkg.height)}${dimensionUnit}`;
}

/** Map packages array Zod errors; when newPackageIndex is set, map that index to "new.*". */
function mapPackagesErrors(error: ZodError, newPackageIndex?: number): Record<string, string> {
	const flat = error.flatten();
	const fieldErrors = flat.fieldErrors as Record<string, string[] | undefined>;
	const out: Record<string, string> = {};
	for (const key of Object.keys(fieldErrors)) {
		const msg = fieldErrors[key]?.[0];
		if (!msg) continue;
		// Keys are like "0", "1.name", "2.length"
		if (newPackageIndex !== undefined && key.startsWith(String(newPackageIndex) + '.')) {
			out['new.' + key.slice(String(newPackageIndex).length + 1)] = msg;
		}
		out[key] = msg;
	}
	return out;
}

export interface PackagePresetsFormProps {
	defaultValue: PackagesFormValue;
	dimensionUnit?: string;
	onChange: (formId: string, value: PackagesFormValue) => void;
	onInteraction?: () => void;
	resetTrigger?: number;
	submitErrors?: Record<string, string>;
	handleSubmit?: (payload: UpdateSettingsPayload) => Promise<UpdateSettingsResponse>;
}

function PackagePresetsFormInner({
	defaultValue,
	dimensionUnit = 'cm',
	onChange,
	onInteraction,
	resetTrigger = 0,
	submitErrors,
	handleSubmit,
}: PackagePresetsFormProps) {
	const [value, setValue] = useState<PackagesFormValue>(() => ({
		packages: defaultValue.packages.map((p) => ({ ...p })),
		defaultPackageId: defaultValue.defaultPackageId,
		newPackage: { ...defaultValue.newPackage },
	}));
	const [errors, setErrors] = useState<Record<string, string>>({});
	const [defaultPackageFeedback, setDefaultPackageFeedback] = useState<
		{ status: 'success' | 'error'; message: string } | null
	>(null);
	const [isDefaultPackageSaving, setIsDefaultPackageSaving] = useState(false);
	const hasInteracted = useRef(false);
	const dimensionBounds = getDimensionBounds(dimensionUnit);

	useEffect(() => {
		setValue({
			packages: defaultValue.packages.map((p) => ({ ...p })),
			defaultPackageId: defaultValue.defaultPackageId,
			newPackage: { ...defaultValue.newPackage },
		});
		setErrors({});
		hasInteracted.current = false;
	}, [defaultValue]);

	useEffect(() => {
		setDefaultPackageFeedback(null);
		hasInteracted.current = false;
	}, [resetTrigger]);

	useEffect(() => {
		const packagesToValidate = [...value.packages];
		const hasNewData =
			value.newPackage.name.trim() !== '' ||
			value.newPackage.length !== 0 ||
			value.newPackage.width !== 0 ||
			value.newPackage.height !== 0;
		if (hasNewData) {
			packagesToValidate.push({
				id: 'pkg_new',
				name: value.newPackage.name.trim(),
				length: value.newPackage.length,
				width: value.newPackage.width,
				height: value.newPackage.height,
				weight: 0,
			});
		}
		const result = packagePresetArraySchema.safeParse(packagesToValidate);
		const nextErrors: Record<string, string> = result.success
			? {}
			: mapPackagesErrors(result.error, hasNewData ? value.packages.length : undefined);

		if (hasNewData) {
			const rangeMessage = buildDimensionRangeMessage(
				dimensionUnit,
				dimensionBounds.min,
				dimensionBounds.max
			);

			const fields: Array<keyof NewPackageForm> = ['length', 'width', 'height'];
			for (const field of fields) {
				const fieldValue = Number(value.newPackage[field]);
				if (
					fieldValue > 0 &&
					(fieldValue < dimensionBounds.min || fieldValue > dimensionBounds.max)
				) {
					nextErrors[`new.${field}`] = rangeMessage;
				}
			}
		}

		if (result.success) {
			setErrors(nextErrors);
		} else {
			setErrors(nextErrors);
		}
	}, [value, dimensionBounds.max, dimensionBounds.min, dimensionUnit]);

	const displayErrors = submitErrors ?? errors;

	const notify = useCallback(
		(next: PackagesFormValue) => {
			if (!hasInteracted.current) {
				hasInteracted.current = true;
				onInteraction?.();
			}
			onChange(PACKAGES_FORM_ID, next);
		},
		[onChange, onInteraction]
	);

	const updateDefaultId = useCallback(
		async (id: string) => {
			if (!handleSubmit || isDefaultPackageSaving) return;
			const previousId = value.defaultPackageId;
			const payload: UpdateSettingsPayload = {
				type: 'App',
				data: { packages: { defaultPackage: id } },
			};
			const next = { ...value, defaultPackageId: id };
			setDefaultPackageFeedback(null);
			setIsDefaultPackageSaving(true);
			setValue(next);
			try {
				const result = await handleSubmit(payload);
				if (result?.success) {
					setDefaultPackageFeedback({
						status: 'success',
						message:
							result.message ??
							__('Default package updated successfully.', 'parcel2go-shipping'),
					});
				} else {
					setValue({ ...value, defaultPackageId: previousId });
					setDefaultPackageFeedback({
						status: 'error',
						message:
							result?.message ??
							__('Failed to update default package', 'parcel2go-shipping'),
					});
				}
			} catch {
				setValue({ ...value, defaultPackageId: previousId });
				setDefaultPackageFeedback({
					status: 'error',
					message: __('Failed to update default package', 'parcel2go-shipping'),
				});
			} finally {
				setIsDefaultPackageSaving(false);
			}
		},
		[value, handleSubmit, isDefaultPackageSaving]
	);

	const updateNewPackage = useCallback(
		(pkg: NewPackageForm) => {
			const next = { ...value, newPackage: pkg };
			setValue(next);
			notify(next);
		},
		[value, notify]
	);

	const defaultOptions = [
		{ value: '', label: __('— Select —', 'parcel2go-shipping') },
		...value.packages.map((pkg) => ({
			value: pkg.id,
			label: formatPackageLabel(pkg, dimensionUnit),
		})),
	];

	return (
		<Flex direction="column" gap={4}>
			<Card size="small" style={{ background: '#fff', boxShadow: '0 1px 3px rgba(0,0,0,0.06)' }}>
				<CardBody>
					<Flex direction="column" gap={3}>
						<div style={{ fontWeight: 600, fontSize: '1rem' }}>
							{__('Default package', 'parcel2go-shipping')}
						</div>
						<SettingsField
							kind="select"
							label={__('Default package', 'parcel2go-shipping')}
							value={value.defaultPackageId}
							onChange={updateDefaultId}
							options={defaultOptions}
							disabled={value.packages.length === 0 || isDefaultPackageSaving}
							description={__(
								'Select the package that will be used by default for new orders.',
								'parcel2go-shipping'
							)}
						/>
						{isDefaultPackageSaving && (
							<Flex align="center" gap={2}>
								<Spinner />
								<span style={{ fontSize: 13, color: '#646970' }}>
									{__('Updating default package...', 'parcel2go-shipping')}
								</span>
							</Flex>
						)}
						{defaultPackageFeedback && (
							<Notice
								status={defaultPackageFeedback.status}
								isDismissible={true}
								onRemove={() => setDefaultPackageFeedback(null)}
							>
								{defaultPackageFeedback.message}
							</Notice>
						)}
					</Flex>
				</CardBody>
			</Card>

			<Card size="small" style={{ background: '#fff', boxShadow: '0 1px 3px rgba(0,0,0,0.06)' }}>
				<CardBody>
					<Flex direction="column" gap={3}>
						<div style={{ fontWeight: 600, fontSize: '1rem' }}>
							{__('Add a new package', 'parcel2go-shipping')}
						</div>
						<SettingsField
							kind="text"
							label={__('Package name *', 'parcel2go-shipping')}
							value={value.newPackage.name}
							onChange={(v) =>
								updateNewPackage({ ...value.newPackage, name: v })
							}
							error={displayErrors['new.name']}
							description={__(
								'Enter a unique name for this package.',
								'parcel2go-shipping'
							)}
						/>
						<div style={{ fontWeight: 500, marginTop: 8 }}>
							{__('Dimensions', 'parcel2go-shipping')} ({dimensionUnit})
						</div>
						<Flex gap={2}>
							<SettingsField
								kind="text"
								label={__('Length *', 'parcel2go-shipping')}
								value={String(value.newPackage.length)}
								onChange={(v) =>
									updateNewPackage({
										...value.newPackage,
										length: parseFloat(v || '0') || 0,
									})
								}
								type="number"
								min={dimensionBounds.min}
								max={dimensionBounds.max}
								error={displayErrors['new.length']}
							/>
							<SettingsField
								kind="text"
								label={__('Width *', 'parcel2go-shipping')}
								value={String(value.newPackage.width)}
								onChange={(v) =>
									updateNewPackage({
										...value.newPackage,
										width: parseFloat(v || '0') || 0,
									})
								}
								type="number"
								min={dimensionBounds.min}
								max={dimensionBounds.max}
								error={displayErrors['new.width']}
							/>
							<SettingsField
								kind="text"
								label={__('Height *', 'parcel2go-shipping')}
								value={String(value.newPackage.height)}
								onChange={(v) =>
									updateNewPackage({
										...value.newPackage,
										height: parseFloat(v || '0') || 0,
									})
								}
								type="number"
								min={dimensionBounds.min}
								max={dimensionBounds.max}
								error={displayErrors['new.height']}
							/>
						</Flex>
						<p style={{ margin: 0, fontSize: 12, color: '#757575' }}>
							{__(
								`Allowed range: ${formatDisplayNumber(dimensionBounds.min)}-${formatDisplayNumber(dimensionBounds.max)} ${dimensionUnit} (1-150cm equivalent).`,
								'parcel2go-shipping'
							)}
						</p>
					</Flex>
				</CardBody>
			</Card>
		</Flex>
	);
}

export const PackagePresetsForm = memo(PackagePresetsFormInner);
