import { createContext, useContext, useEffect, useRef, useState } from 'react';
import { DRAW_CURSOR_CSS } from './drawCursors';
import { IconChevronDown, IconLock, IconLockOpen } from '@tabler/icons-react';
import { usePrintEditorStore } from '../state/usePrintEditorStore';

/* ------------------------------------------------------------------ *
 *  Figma UI3 design tokens (light / dark)
 *  Chrome font: Inter 11px · accent #0d99ff · hairline #e6e6e6
 * ------------------------------------------------------------------ */

export const T = {
    panel: 'bg-white dark:bg-[#2c2c2c]',
    border: 'border-[#e6e6e6] dark:border-[#444444]',
    divide: 'divide-[#e6e6e6] dark:divide-[#444444]',
    text: 'text-[#000000] dark:text-[#ffffff]',
    textSoft: 'text-black/50 dark:text-white/50',
    textFaint: 'text-black/30 dark:text-white/30',
    inputBg: 'bg-[#f5f5f5] dark:bg-[#383838]',
    hoverBg: 'hover:bg-[#f5f5f5] dark:hover:bg-[#383838]',
    activeBg: 'bg-[#f5f5f5] dark:bg-[#383838]',
    selectedRow: 'bg-[#e5f4ff] dark:bg-[#0d99ff]/20',
    blue: '#0d99ff',
};

/** 11px Inter body — apply once on the editor shell */
export const chromeFont = {
    fontFamily: 'Inter, "Noto Sans Bengali", system-ui, -apple-system, sans-serif',
};

/* ------------------------------------------------------------------ *
 *  Icon button
 * ------------------------------------------------------------------ */

export function IconBtn({
    icon: Icon,
    label,
    onClick,
    active = false,
    disabled = false,
    size = 16,
    className = '',
    children,
    ...rest
}) {
    return (
        <button
            type="button"
            title={label}
            aria-label={label}
            disabled={disabled}
            onClick={onClick}
            className={`h-6 w-6 shrink-0 flex items-center justify-center rounded-[5px] transition-colors
                disabled:opacity-30 disabled:pointer-events-none
                ${active
                    ? 'bg-[#e5f4ff] text-[#0d99ff] dark:bg-[#0d99ff]/25 dark:text-[#7cc4f8]'
                    : `${T.hoverBg} text-black/80 dark:text-white/80`}
                ${className}`}
            {...rest}
        >
            {Icon ? <Icon size={size} stroke={1.75} /> : children}
        </button>
    );
}

/* ------------------------------------------------------------------ *
 *  Panel section — hairline divider, 11px semibold title, action slot
 * ------------------------------------------------------------------ */

/**
 * Set by the panel around a section whose lock key it knows, so a section can
 * carry its own lock without every component threading a prop down to its
 * `PanelSection`. `{ locked, onToggle }`, or null where there is no lock to
 * offer — a user's panel, or a section outside the switchboard.
 */
export const SectionLockContext = createContext(null);

export function PanelSection({ title, actions = null, children, className = '' }) {
    const lock = useContext(SectionLockContext);
    return (
        <div className={`group/section border-b ${T.border} px-4 py-2.5 ${className}`}>
            {title && (
                <div className="h-6 mb-1 flex items-center justify-between">
                    <span className={`text-[11px] font-semibold ${T.text}`}>{title}</span>
                    {(actions || lock) && (
                        <div className="flex items-center gap-0.5 -mr-1.5">
                            {actions}
                            {lock && (
                                <button
                                    type="button"
                                    onClick={lock.onToggle}
                                    title={lock.locked
                                        ? `${title} is hidden from the user — click to open it`
                                        : `${title} is open to the user — click to lock it`}
                                    aria-label={lock.locked ? `Unlock ${title} for the user` : `Lock ${title} for the user`}
                                    // An open section shows its lock only on hover: every
                                    // section would otherwise wear an icon saying nothing.
                                    className={`h-6 w-6 shrink-0 flex items-center justify-center rounded-[5px] transition-colors
                                        ${lock.locked
                                            ? 'text-[#0d99ff] bg-[#0d99ff]/10'
                                            : `${T.textFaint} opacity-0 group-hover/section:opacity-100 focus-visible:opacity-100 hover:!text-black dark:hover:!text-white`}`}
                                >
                                    {lock.locked ? <IconLock size={12} /> : <IconLockOpen size={12} />}
                                </button>
                            )}
                        </div>
                    )}
                </div>
            )}
            {children}
        </div>
    );
}

/** Two-column property row (Figma pairs inputs side by side) */
export function Row({ children, cols = 2, className = '' }) {
    return (
        <div
            className={`grid gap-2 mb-2 last:mb-0 ${className}`}
            style={{ gridTemplateColumns: `repeat(${cols}, minmax(0,1fr))` }}
        >
            {children}
        </div>
    );
}

export function RowLabel({ children }) {
    return <div className={`text-[11px] ${T.textSoft} mb-1`}>{children}</div>;
}

/** A labelled control — Figma captions each group above its inputs. */
export function Field({ label, children, className = '' }) {
    return (
        <div className={`min-w-0 mb-2 last:mb-0 ${className}`}>
            {label && <RowLabel>{label}</RowLabel>}
            {children}
        </div>
    );
}

/* ------------------------------------------------------------------ *
 *  Numeric input — label prefix, scrub-drag, arrow keys, commit on blur
 * ------------------------------------------------------------------ */

function beginGesture() {
    try { usePrintEditorStore.getState().beginHistoryGesture(); } catch { /* no store in tests */ }
}
function endGesture() {
    try { usePrintEditorStore.getState().endHistoryGesture(); } catch { /* ignore */ }
}

export function NumInput({
    label,
    icon: Icon,
    value,
    onChange,
    min = -Infinity,
    max = Infinity,
    step = 1,
    precision = null,
    suffix = '',
    disabled = false,
    title,
    className = '',
}) {
    const [draft, setDraft] = useState(null);
    const scrubRef = useRef(null);
    const draftBaseRef = useRef(null); // value snapshot when the current draft began

    const round = (v) => {
        if (precision == null) return Math.round(v * 100) / 100;
        const f = 10 ** precision;
        return Math.round(v * f) / f;
    };
    const clamp = (v) => Math.min(max, Math.max(min, v));
    const shown = draft != null
        ? draft
        : `${Number.isFinite(Number(value)) ? round(Number(value)) : 0}${suffix}`;

    const commit = (raw) => {
        const num = parseFloat(String(raw).replace(suffix, ''));
        if (Number.isFinite(num)) onChange(clamp(round(num)));
        setDraft(null);
    };

    // If the value changes from an external source (canvas drag, slider, alignment,
    // another control) while this field holds an edit draft, drop the draft so the
    // display follows the live value instead of freezing on the stale text.
    useEffect(() => {
        if (draft != null && round(Number(value) || 0) !== draftBaseRef.current) {
            setDraft(null);
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [value]);

    // Scrub: drag horizontally on the label to change the value (Figma behavior)
    const onScrubDown = (e) => {
        if (disabled) return;
        e.preventDefault();
        // Drop any focused-edit draft so the field shows the live scrubbed value
        // instead of freezing on the stale draft string.
        setDraft(null);
        const startX = e.clientX;
        const startVal = Number(value) || 0;
        beginGesture();
        scrubRef.current = { startX, startVal };
        const move = (ev) => {
            const s = scrubRef.current;
            if (!s) return;
            setDraft(null); // stay bound to the live value if focus lands mid-drag
            // Snap to whole `step` increments, and quantise the result onto that grid,
            // so dragging yields clean values (19, 20, 21…) instead of arbitrary
            // decimals like 18.74. Sensitivity is unchanged: 2px of travel = one step.
            const unit = (ev.shiftKey ? step * 10 : step) || 1;
            const steps = Math.round((ev.clientX - s.startX) * 0.5);
            const next = Math.round((s.startVal + steps * unit) / unit) * unit;
            onChange(clamp(round(next)));
        };
        const up = () => {
            scrubRef.current = null;
            endGesture();
            window.removeEventListener('pointermove', move);
            window.removeEventListener('pointerup', up);
        };
        window.addEventListener('pointermove', move);
        window.addEventListener('pointerup', up);
    };

    const onKeyDown = (e) => {
        if (e.key === 'Enter') { commit(e.currentTarget.value); e.currentTarget.blur(); return; }
        if (e.key === 'Escape') { setDraft(null); e.currentTarget.blur(); return; }
        if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
            e.preventDefault();
            const dir = e.key === 'ArrowUp' ? 1 : -1;
            const amount = (e.shiftKey ? step * 10 : step) * dir;
            const base = draft != null ? parseFloat(draft) : Number(value) || 0;
            const next = clamp(round((Number.isFinite(base) ? base : 0) + amount));
            setDraft(null);
            onChange(next);
        }
    };

    return (
        <label
            title={title || label}
            className={`group flex items-center h-7 rounded-[5px] border border-transparent
                ${T.inputBg} ${disabled ? 'opacity-40' : 'hover:border-[#e6e6e6] dark:hover:border-[#555555]'}
                focus-within:!border-[#0d99ff] focus-within:ring-1 focus-within:ring-[#0d99ff] transition-colors ${className}`}
        >
            {(label || Icon) && (
                <span
                    onPointerDown={onScrubDown}
                    className={`pl-2 pr-1 text-[11px] ${T.textFaint} select-none shrink-0 flex items-center
                        ${disabled ? '' : 'cursor-ew-resize'}`}
                >
                    {Icon ? <Icon size={12} stroke={1.75} /> : label}
                </span>
            )}
            <input
                type="text"
                inputMode="decimal"
                disabled={disabled}
                className={`w-full min-w-0 h-full bg-transparent outline-none text-[11px] ${T.text} ${label || Icon ? 'pr-2' : 'px-2'}`}
                value={shown}
                onFocus={(e) => {
                    const base = round(Number(value) || 0);
                    draftBaseRef.current = base;
                    setDraft(String(base));
                    requestAnimationFrame(() => e.target.select?.());
                }}
                onChange={(e) => setDraft(e.target.value)}
                onBlur={(e) => commit(e.target.value)}
                onKeyDown={onKeyDown}
            />
        </label>
    );
}

/* ------------------------------------------------------------------ *
 *  Text input / textarea (Figma filled style)
 * ------------------------------------------------------------------ */

export const figInputCls = `w-full h-7 px-2 text-[11px] rounded-[5px] border border-transparent outline-none
    bg-[#f5f5f5] dark:bg-[#383838] text-black dark:text-white
    hover:border-[#e6e6e6] dark:hover:border-[#555555]
    focus:!border-[#0d99ff] focus:ring-1 focus:ring-[#0d99ff] transition-colors
    disabled:opacity-40 placeholder:text-black/30 dark:placeholder:text-white/30`;

export const figTextareaCls = `w-full min-h-[6rem] px-2 py-1.5 text-[11px] leading-normal rounded-[5px] border border-transparent outline-none
    bg-[#f5f5f5] dark:bg-[#383838] text-black dark:text-white resize-y
    hover:border-[#e6e6e6] dark:hover:border-[#555555]
    focus:!border-[#0d99ff] focus:ring-1 focus:ring-[#0d99ff] transition-colors
    disabled:opacity-40 placeholder:text-black/30 dark:placeholder:text-white/30`;

/* ------------------------------------------------------------------ *
 *  Select — native element, Figma closed-state chrome
 * ------------------------------------------------------------------ */

export function FigSelect({ value, onChange, options, disabled = false, title, className = '', style }) {
    const current = options.find((o) => String(o.value) === String(value));
    return (
        <div className={`relative ${className}`} title={title}>
            <select
                value={value}
                disabled={disabled}
                onChange={(e) => onChange(e.target.value)}
                style={{ ...current?.style, ...style }}
                className={`w-full h-7 py-0 pl-2 pr-6 text-[11px] leading-none rounded-[5px] border border-transparent outline-none appearance-none cursor-pointer
                    !bg-none bg-[#f5f5f5] dark:bg-[#383838] text-black dark:text-white
                    hover:border-[#e6e6e6] dark:hover:border-[#555555]
                    focus:border-[#0d99ff] focus:ring-1 focus:ring-[#0d99ff] transition-colors disabled:opacity-40`}
            >
                {options.map((o) => (
                    <option key={o.value} value={o.value} style={o.style}>{o.label}</option>
                ))}
            </select>
            <IconChevronDown
                size={12}
                className="absolute right-1.5 top-1/2 -translate-y-1/2 pointer-events-none text-black/50 dark:text-white/50"
            />
        </div>
    );
}

/* ------------------------------------------------------------------ *
 *  Segmented icon group — alignment rows etc.
 * ------------------------------------------------------------------ */

export function IconSegment({ options, value, onChange, disabled = false, className = '', allowUnset = false }) {
    return (
        <div className={`flex items-center rounded-[5px] p-0.5 ${T.inputBg} ${className}`} role="tablist">
            {options.map(({ value: v, icon: Icon, label, render }) => {
                const active = String(v) === String(value);
                return (
                    <button
                        key={String(v)}
                        type="button"
                        role="tab"
                        aria-selected={active}
                        title={label}
                        disabled={disabled}
                        onClick={() => onChange(allowUnset && active ? null : v)}
                        className={`flex-1 h-6 flex items-center justify-center rounded-[4px] transition-colors disabled:opacity-40
                            ${active
                                ? 'bg-white dark:bg-[#555555] text-black dark:text-white shadow-sm'
                                : 'text-black/50 dark:text-white/50 hover:text-black dark:hover:text-white'}`}
                    >
                        {render ? render(active) : <Icon size={14} stroke={1.75} />}
                    </button>
                );
            })}
        </div>
    );
}

/* ------------------------------------------------------------------ *
 *  Segmented text group — a short, fixed set of choices laid out as
 *  tabs instead of hidden behind a dropdown. The selected tab is ringed
 *  in the accent blue so the current value is readable at a glance
 *  rather than only after opening a menu.
 * ------------------------------------------------------------------ */

export function FigSegment({ options, value, onChange, disabled = false, title, className = '' }) {
    return (
        <div title={title} className={`flex items-center rounded-[5px] p-0.5 gap-0.5 ${T.inputBg} ${className}`} role="tablist">
            {options.map(({ value: v, label }) => {
                const active = String(v) === String(value);
                return (
                    <button
                        key={String(v)}
                        type="button"
                        role="tab"
                        aria-selected={active}
                        disabled={disabled}
                        onClick={() => onChange(v)}
                        className={`flex-1 min-w-0 h-6 px-1.5 text-[11px] leading-none rounded-[4px] border truncate transition-colors disabled:opacity-40
                            ${active
                                ? 'border-[#0d99ff] bg-white dark:bg-[#2c2c2c] text-black dark:text-white font-medium'
                                : 'border-transparent text-black/50 dark:text-white/50 hover:text-black dark:hover:text-white'}`}
                    >
                        {label}
                    </button>
                );
            })}
        </div>
    );
}

/* ------------------------------------------------------------------ *
 *  Checkbox — small square, blue when on (Figma "Clip content" style)
 * ------------------------------------------------------------------ */

export function FigCheck({ label, checked, onChange, disabled = false }) {
    return (
        <label className={`flex items-center gap-2 h-6 select-none ${disabled ? 'opacity-40' : 'cursor-pointer'}`}>
            <input
                type="checkbox"
                className="peer sr-only"
                checked={!!checked}
                disabled={disabled}
                onChange={(e) => onChange(e.target.checked)}
            />
            <span
                className={`h-3.5 w-3.5 shrink-0 rounded-[3px] border flex items-center justify-center transition-colors
                    ${checked
                        ? 'bg-[#0d99ff] border-[#0d99ff]'
                        : 'bg-white dark:bg-[#383838] border-black/30 dark:border-white/30'}`}
            >
                {checked && (
                    <svg width="8" height="8" viewBox="0 0 10 10" fill="none">
                        <path d="M1.5 5.5L4 8L8.5 2.5" stroke="white" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
                    </svg>
                )}
            </span>
            <span className={`text-[11px] ${T.text}`}>{label}</span>
        </label>
    );
}

/* ------------------------------------------------------------------ *
 *  Slider — thin track + round thumb (image adjust popover)
 * ------------------------------------------------------------------ */

export function FigSlider({ label, value, onChange, min = 0, max = 100, step = 1, defaultValue = null, disabled = false }) {
    const pct = ((Number(value) - min) / (max - min)) * 100;
    return (
        <div className="flex items-center gap-2 h-7">
            <span className={`w-[72px] shrink-0 text-[11px] truncate ${T.textSoft}`} title={label}>{label}</span>
            <div className="relative flex-1 flex items-center h-full">
                <input
                    type="range"
                    min={min}
                    max={max}
                    step={step}
                    value={Number(value)}
                    disabled={disabled}
                    onPointerDown={beginGesture}
                    onPointerUp={endGesture}
                    onPointerCancel={endGesture}
                    onDoubleClick={() => defaultValue != null && onChange(defaultValue)}
                    onChange={(e) => onChange(Number(e.target.value))}
                    className="fig-slider w-full"
                    style={{ '--fill': `${Math.min(100, Math.max(0, pct))}%` }}
                />
            </div>
            <NumInput
                value={value}
                onChange={onChange}
                min={min}
                max={max}
                step={step}
                disabled={disabled}
                className="w-12 shrink-0"
            />
        </div>
    );
}

/** Scoped CSS for range inputs + scrollbars — injected once by the shell */
export function EditorGlobalStyles() {
    useEffect(() => {
        if (document.getElementById('fig-editor-css')) return;
        const el = document.createElement('style');
        el.id = 'fig-editor-css';
        el.textContent = `${DRAW_CURSOR_CSS}
.fig-slider { -webkit-appearance: none; appearance: none; height: 12px; background: transparent; cursor: pointer; }
.fig-slider:disabled { opacity: .4; cursor: default; }
.fig-slider::-webkit-slider-runnable-track {
    height: 4px; border-radius: 2px;
    background: linear-gradient(to right, #0d99ff var(--fill, 50%), rgba(0,0,0,.12) var(--fill, 50%));
}
.dark .fig-slider::-webkit-slider-runnable-track,
[data-mantine-color-scheme="dark"] .fig-slider::-webkit-slider-runnable-track {
    background: linear-gradient(to right, #0d99ff var(--fill, 50%), rgba(255,255,255,.18) var(--fill, 50%));
}
.fig-slider::-webkit-slider-thumb {
    -webkit-appearance: none; appearance: none; width: 12px; height: 12px; margin-top: -4px;
    border-radius: 50%; background: #ffffff; border: 1px solid rgba(0,0,0,.18);
    box-shadow: 0 1px 3px rgba(0,0,0,.25);
}
.fig-slider::-moz-range-track { height: 4px; border-radius: 2px; background: rgba(0,0,0,.12); }
.fig-slider::-moz-range-progress { height: 4px; border-radius: 2px; background: #0d99ff; }
.fig-slider::-moz-range-thumb {
    width: 11px; height: 11px; border-radius: 50%; background: #fff;
    border: 1px solid rgba(0,0,0,.18); box-shadow: 0 1px 3px rgba(0,0,0,.25);
}
.fig-scroll::-webkit-scrollbar { width: 8px; height: 8px; }
.fig-scroll::-webkit-scrollbar-thumb { background: rgba(0,0,0,.15); border-radius: 4px; background-clip: padding-box; border: 2px solid transparent; }
.fig-scroll::-webkit-scrollbar-thumb:hover { background-color: rgba(0,0,0,.3); }
.dark .fig-scroll::-webkit-scrollbar-thumb,
[data-mantine-color-scheme="dark"] .fig-scroll::-webkit-scrollbar-thumb { background-color: rgba(255,255,255,.2); }
.fig-scroll { scrollbar-width: thin; scrollbar-color: rgba(0,0,0,.2) transparent; }
/* Caret for on-path text editing — the real textarea caret is hidden there, so
   the SVG run draws its own, blinking on the same 1s beat browsers use. */
@keyframes fig-caret-blink { 0%, 49% { opacity: 1 } 50%, 100% { opacity: 0 } }
.fig-caret { animation: fig-caret-blink 1s step-end infinite; }
/* The on-path field is a transparent textarea holding the text as a flat block,
   so the browser would paint its selection where the letters are NOT. Hide it —
   the SVG run highlights the selected glyphs on the curve instead. */
.fig-path-input::selection { background: transparent; }
`;
        document.head.appendChild(el);
    }, []);
    return null;
}
