import { cn } from '@/lib/utils';
import * as React from 'react';

interface TimePickerProps {
	value?: string;
	onChange?: (value: string) => void;
	id?: string;
	disabled?: boolean;
	className?: string;
}

function clamp(v: number, min: number, max: number) {
	return Math.max(min, Math.min(max, v));
}

function pad(n: number) {
	return String(n).padStart(2, '0');
}

function parse24(value: string | undefined) {
	if (!value) return { h: 12, m: 0, period: 'AM' as const };
	const [hStr, mStr] = value.split(':');
	const h24 = clamp(parseInt(hStr ?? '0', 10) || 0, 0, 23);
	const m = clamp(parseInt(mStr ?? '0', 10) || 0, 0, 59);
	const period = h24 < 12 ? ('AM' as const) : ('PM' as const);
	const h12 = h24 % 12 === 0 ? 12 : h24 % 12;
	return { h: h12, m, period };
}

function to24(h12: number, m: number, period: 'AM' | 'PM') {
	let h24 = h12 % 12;
	if (period === 'PM') h24 += 12;
	return `${pad(h24)}:${pad(m)}`;
}

export function TimePicker({ value, onChange, id, disabled, className }: TimePickerProps) {
	const { h, m, period } = parse24(value);

	// Use refs for buffer values — updated synchronously before any focus() call
	// so blur handlers always see the up-to-date value.
	const hourBufRef = React.useRef('');
	const minBufRef = React.useRef('');
	const [hourBufDisplay, setHourBufDisplay] = React.useState('');
	const [minBufDisplay, setMinBufDisplay] = React.useState('');

	const hourRef = React.useRef<HTMLDivElement>(null);
	const minRef = React.useRef<HTMLDivElement>(null);
	const periodRef = React.useRef<HTMLDivElement>(null);

	const emit = (nh: number, nm: number, np: 'AM' | 'PM') => onChange?.(to24(nh, nm, np));

	const clearHourBuf = () => { hourBufRef.current = ''; setHourBufDisplay(''); };
	const clearMinBuf = () => { minBufRef.current = ''; setMinBufDisplay(''); };

	// ── Hour ──────────────────────────────────────────────────────────────
	const handleHourKey = (e: React.KeyboardEvent) => {
		if (e.key === 'ArrowUp') { e.preventDefault(); emit(h === 12 ? 1 : h + 1, m, period); return; }
		if (e.key === 'ArrowDown') { e.preventDefault(); emit(h === 1 ? 12 : h - 1, m, period); return; }
		if (e.key === 'Tab') return;
		if (e.key === 'a' || e.key === 'A') { e.preventDefault(); emit(h, m, 'AM'); return; }
		if (e.key === 'p' || e.key === 'P') { e.preventDefault(); emit(h, m, 'PM'); return; }

		const digit = e.key.match(/^[0-9]$/) ? e.key : null;
		if (!digit) return;
		e.preventDefault();

		const next = hourBufRef.current + digit;
		const num = parseInt(next, 10);

		if (next.length === 1 && num > 1) {
			clearHourBuf();
			emit(clamp(num, 1, 12), m, period);
			minRef.current?.focus();
		} else if (next.length >= 2) {
			clearHourBuf();
			emit(clamp(num, 1, 12), m, period);
			minRef.current?.focus();
		} else {
			hourBufRef.current = next;
			setHourBufDisplay(next);
		}
	};

	const handleHourBlur = () => {
		const buf = hourBufRef.current;
		if (buf) {
			clearHourBuf();
			emit(clamp(parseInt(buf, 10) || 12, 1, 12), m, period);
		}
	};

	// ── Minute ────────────────────────────────────────────────────────────
	const handleMinKey = (e: React.KeyboardEvent) => {
		if (e.key === 'ArrowUp') { e.preventDefault(); emit(h, m === 59 ? 0 : m + 1, period); return; }
		if (e.key === 'ArrowDown') { e.preventDefault(); emit(h, m === 0 ? 59 : m - 1, period); return; }
		if (e.key === 'Tab') return;

		const digit = e.key.match(/^[0-9]$/) ? e.key : null;
		if (!digit) return;
		e.preventDefault();

		const next = minBufRef.current + digit;
		const num = parseInt(next, 10);

		if (next.length >= 2) {
			clearMinBuf();
			emit(h, clamp(num, 0, 59), period);
			periodRef.current?.focus();
		} else {
			minBufRef.current = next;
			setMinBufDisplay(next);
		}
	};

	const handleMinBlur = () => {
		const buf = minBufRef.current;
		if (buf) {
			clearMinBuf();
			emit(h, clamp(parseInt(buf, 10) || 0, 0, 59), period);
		}
	};

	// ── Period ────────────────────────────────────────────────────────────
	const handlePeriodKey = (e: React.KeyboardEvent) => {
		if (['ArrowUp', 'ArrowDown', ' '].includes(e.key)) { e.preventDefault(); emit(h, m, period === 'AM' ? 'PM' : 'AM'); }
		if (e.key === 'a' || e.key === 'A') { e.preventDefault(); emit(h, m, 'AM'); }
		if (e.key === 'p' || e.key === 'P') { e.preventDefault(); emit(h, m, 'PM'); }
		if (e.key === 'Tab' && e.shiftKey) { e.preventDefault(); minRef.current?.focus(); }
	};

	const segClass =
		'cursor-default select-none rounded-sm px-1 text-sm tabular-nums focus:bg-accent focus:outline-none';

	const displayH = hourBufDisplay || pad(h);
	const displayM = minBufDisplay || pad(m);

	return (
		<div
			className={cn(
				'border-input focus-within:border-ring focus-within:ring-ring/50 flex h-9 w-full items-center gap-0.5 rounded-md border bg-white px-3 shadow-xs transition-[color,box-shadow] focus-within:ring-[3px]',
				disabled && 'pointer-events-none opacity-50',
				className,
			)}
			onClick={(e) => { if (e.target === e.currentTarget) hourRef.current?.focus(); }}
		>
			<div
				ref={hourRef}
				id={id}
				role="spinbutton"
				tabIndex={disabled ? -1 : 0}
				aria-label="Hours"
				aria-valuenow={h}
				aria-valuemin={1}
				aria-valuemax={12}
				className={segClass}
				onKeyDown={handleHourKey}
				onBlur={handleHourBlur}
			>
				{displayH}
			</div>

			<span className="text-muted-foreground select-none text-sm">:</span>

			<div
				ref={minRef}
				role="spinbutton"
				tabIndex={disabled ? -1 : 0}
				aria-label="Minutes"
				aria-valuenow={m}
				aria-valuemin={0}
				aria-valuemax={59}
				className={segClass}
				onKeyDown={handleMinKey}
				onBlur={handleMinBlur}
			>
				{displayM}
			</div>

			<span className="text-muted-foreground mx-0.5 select-none text-sm">·</span>

			<div
				ref={periodRef}
				role="spinbutton"
				tabIndex={disabled ? -1 : 0}
				aria-label="AM/PM"
				className={cn(segClass, 'cursor-pointer text-xs font-semibold uppercase')}
				onKeyDown={handlePeriodKey}
				onClick={(e) => { e.stopPropagation(); emit(h, m, period === 'AM' ? 'PM' : 'AM'); }}
			>
				{period}
			</div>
		</div>
	);
}
