import type { AsukaModuleComponent } from '../../core/modules';
import { useT } from '../../hooks/useT';
import { getI18n } from '../../core/i18n';
import { registerModule } from '../../core/modules';
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
import { useAsukaStore } from '../../store/asukaStore';
import { Card, CardContent, CardHeader } from '../core/Card';
import { Field } from '../core/Field';
import { Input } from '../core/Input';
import { Button } from '../core/Button';

type CurveType = 'arch-up' | 'arch-down' | 'arch-left' | 'arch-right' | 'wave' | 'inflate' | 'deflate' | 'circle';

const CURVE_PRESETS: Array<{
  value: CurveType;
  key: string;
  fallback: string;
  path?: string;
  circle?: boolean;
}> = [
  { value: 'arch-up', key: 'textCurve.archUp', fallback: 'Arch up', path: 'M4 17 Q24 2 44 17' },
  { value: 'arch-down', key: 'textCurve.archDown', fallback: 'Arch down', path: 'M4 5 Q24 20 44 5' },
  { value: 'arch-left', key: 'textCurve.archLeft', fallback: 'Arch left', path: 'M31 2 Q12 11 31 20' },
  { value: 'arch-right', key: 'textCurve.archRight', fallback: 'Arch right', path: 'M17 2 Q36 11 17 20' },
  { value: 'wave', key: 'textCurve.wave', fallback: 'Wave', path: 'M3 12 Q13 2 24 11 Q35 20 45 10' },
  { value: 'inflate', key: 'textCurve.inflate', fallback: 'Inflate', path: 'M4 17 C8 2 40 2 44 17' },
  { value: 'deflate', key: 'textCurve.deflate', fallback: 'Deflate', path: 'M4 5 C8 20 40 20 44 5' },
  { value: 'circle', key: 'textCurve.circle', fallback: 'Circle', circle: true },
];

const clampBend = (value: number): number => Math.min(100, Math.max(0, value || 0));

const TextCurveModule: AsukaModuleComponent = ({ context }) => {
  const t = useT();
  const [curved, setCurved] = useState(false);
  const [type, setType] = useState<CurveType>('arch-up');
  const [bend, setBend] = useState<number>(30);
  const [editText, setEditText] = useState<string>('');
  const [hasSelection, setHasSelection] = useState(false);
  const engine = context.getEngine?.() ?? null;
  const textTools = engine?.canvas2d ?? null;

  const syncRafRef = useRef<number | null>(null);
  const previewRafRef = useRef<number | null>(null);
  const pendingPatchRef = useRef<{ type?: CurveType; bend?: number; text?: string } | null>(null);

  const syncFromSelection = useCallback(() => {
    const selection = textTools?.getSelectedText() ?? null;
    setHasSelection(!!selection);
    setCurved(selection?.curved === true);
    setType((selection?.curveType as CurveType) || 'arch-up');
    setBend(typeof selection?.curveBend === 'number' ? selection.curveBend : 30);
    setEditText(selection?.text ?? '');
  }, [textTools]);

  useEffect(() => {
    syncFromSelection();

    if (!textTools) return;

    const onSel = () => {
      if (syncRafRef.current) cancelAnimationFrame(syncRafRef.current);
      syncRafRef.current = requestAnimationFrame(() => {
        syncRafRef.current = null;
        syncFromSelection();
      });
    };

    const unsubscribe = engine?.events.subscribe(() => onSel()) ?? (() => {});

    return () => {
      if (syncRafRef.current) cancelAnimationFrame(syncRafRef.current);
      if (previewRafRef.current) cancelAnimationFrame(previewRafRef.current);
      unsubscribe();
    };
  }, [engine, textTools, syncFromSelection]);

  const applyPreview = (patch: { type?: CurveType; bend?: number; text?: string }) => {
    if (!textTools) return;

    // Merge patches and apply at most once per animation frame.
    pendingPatchRef.current = { ...(pendingPatchRef.current || {}), ...patch };
    if (previewRafRef.current) return;
    previewRafRef.current = requestAnimationFrame(() => {
      previewRafRef.current = null;
      const p = pendingPatchRef.current;
      pendingPatchRef.current = null;
      if (!p) return;
      void textTools.updateSelectedTextCurve({
          type: p.type ?? type,
          bend: typeof p.bend === 'number' ? p.bend : bend,
          text: p.text ?? editText,
        }, { preview: true, source: 'text-curve-preview' }).catch(() => {});
    });
  };

  const commitNow = (patch: { type?: CurveType; bend?: number; text?: string } = {}) => {
    if (!textTools) return;
    if (previewRafRef.current) cancelAnimationFrame(previewRafRef.current);
    previewRafRef.current = null;
    pendingPatchRef.current = null;
    void textTools.updateSelectedTextCurve({
        type: patch.type ?? type,
        bend: typeof patch.bend === 'number' ? patch.bend : bend,
        text: patch.text ?? editText,
      }, { commit: true, source: 'text-curve' }).catch(() => {});
  };

  const reopenSelectionToolbarModule = () => {
    // Use the store singleton to avoid stale closures.
    const st = useAsukaStore.getState();
    try {
      st.openSelectionMenu?.();
      const last = st.selectionMenuStack?.[st.selectionMenuStack.length - 1];
      if (last !== 'text-curve') {
        st.selectionMenuGoTo?.('text-curve' as any);
      }
    } catch {
      // ignore
    }
 };

  const scheduleReopen = () => {
    // Selection changes can happen across multiple ticks (clear -> create/updated).
    // Try a couple of times to guarantee the module is shown.
    window.setTimeout(reopenSelectionToolbarModule, 0);
    window.setTimeout(reopenSelectionToolbarModule, 60);
  };

  return (
    <Card className="ask-w-full" onPointerDown={(e) => e.stopPropagation()}>
      <CardHeader title={t('ui.curved_text')} />
      <CardContent>
        <div className="ask-flex ask-gap-2">
          <Button
            variant="primary"
            disabled={!hasSelection}
            onClick={() => {
              if (!textTools) return;
              void textTools.setSelectedTextCurved(!curved, { type, bend, text: editText || undefined })
                .then(() => syncFromSelection())
                .catch(() => {});

              scheduleReopen();
            }}
          >
            {curved
              ? t('textCurve.makeStraight', 'Make straight')
              : t('textCurve.makeCurved', 'Curve text')}
          </Button>
        </div>

        {curved ? (
          <div className="ask-mt-4 ask-flex ask-flex-col ask-gap-4">
            <Field label={t('ui.curve_type')}>
              <div className="ask-grid ask-grid-cols-2 ask-gap-2">
                {CURVE_PRESETS.map((preset) => {
                  const active = type === preset.value;
                  const label = t(preset.key, preset.fallback);
                  return (
                    <button
                      key={preset.value}
                      type="button"
                      aria-label={label}
                      aria-pressed={active}
                      title={label}
                      className={`ask-flex ask-min-h-14 ask-items-center ask-gap-2 ask-rounded-lg ask-border ask-px-2.5 ask-py-2 ask-text-left ask-transition-colors ${
                        active
                          ? 'ask-border-primary ask-bg-primary/10 ask-text-primary'
                          : 'ask-border-borderSubtle ask-bg-surface hover:ask-border-primary/30 hover:ask-bg-surfaceAlt'
                      }`}
                      onClick={() => {
                        setType(preset.value);
                        commitNow({ type: preset.value });
                      }}
                    >
                      <svg
                        aria-hidden="true"
                        viewBox="0 0 48 22"
                        className="ask-h-6 ask-w-12 ask-shrink-0"
                        fill="none"
                        stroke="currentColor"
                        strokeWidth="2"
                        strokeLinecap="round"
                      >
                        {preset.circle ? <circle cx="24" cy="11" r="8" /> : <path d={preset.path} />}
                      </svg>
                      <span className="ask-text-[11px] ask-font-medium ask-leading-4">{label}</span>
                    </button>
                  );
                })}
              </div>
            </Field>

            <Field label={t('ui.bend')}>
              <div className="ask-flex ask-items-center ask-gap-2">
                <input
                  type="range"
                  min={0}
                  max={100}
                  step={1}
                  value={bend}
                  className="ask-px-0 ask-h-8 ask-flex-1"
                  onPointerDown={(e) => e.stopPropagation()}
                  onMouseDown={(e) => e.stopPropagation()}
                  onTouchStart={(e) => e.stopPropagation()}
                  onInput={(e) => {
                    const v = clampBend(Number((e.currentTarget as HTMLInputElement).value));
                    setBend(v);
                    applyPreview({ bend: v });
                  }}
                  onChange={(e) => {
                    const v = clampBend(Number((e.currentTarget as HTMLInputElement).value));
                    setBend(v);
                    commitNow({ bend: v });
                  }}
                />
                <div className="ask-relative ask-w-20 ask-shrink-0">
                  <Input
                    compact
                    type="number"
                    min={0}
                    max={100}
                    step={1}
                    value={bend}
                    aria-label={t('textCurve.bendValue', 'Curve strength')}
                    className="ask-pr-6 ask-text-right"
                    onInput={(e) => {
                      const v = clampBend(Number((e.currentTarget as HTMLInputElement).value));
                      setBend(v);
                      applyPreview({ bend: v });
                    }}
                    onChange={(e) => {
                      const v = clampBend(Number((e.currentTarget as HTMLInputElement).value));
                      setBend(v);
                      commitNow({ bend: v });
                    }}
                  />
                  <span className="ask-pointer-events-none ask-absolute ask-right-2 ask-top-1/2 -ask-translate-y-1/2 ask-text-[10px] ask-text-textMuted">%</span>
                </div>
              </div>
            </Field>

            <Field label={t('ui.text')}>
              <Input
                value={editText}
                placeholder={t('ui.edit_text')}
                onInput={(e: any) => {
                  const v = String(e?.currentTarget?.value ?? '');
                  setEditText(v);
                  applyPreview({ text: v });
                }}
                onChange={(e: any) => {
                  const v = String(e?.currentTarget?.value ?? '');
                  setEditText(v);
                  commitNow({ text: v });
                }}
              />
            </Field>
          </div>
        ) : null}
      </CardContent>
    </Card>
  );
};


registerModule({
  id: 'text-curve',
  label: getI18n().t('textCurve.title', 'Curved text'),
  icon: 'curve-text',
  displayMode: 'floating',
  render: TextCurveModule,
});

export default TextCurveModule;
