import { useEffect, useMemo, useState } from 'preact/hooks';
import { registerModule, type AsukaModuleContext } from '../../core/modules';
import { useAsukaStore } from '../../store/asukaStore';
import { DEFAULT_PERMISSIONS } from '../../core/permissions';
import { useT } from '../../hooks/useT';

function clamp(n: number, a: number, b: number): number {
  return Math.min(b, Math.max(a, n));
}

function ZoomChip({
  label,
  onClick,
  disabled,
  title,
}: {
  label: string;
  onClick: () => void;
  disabled?: boolean;
  title?: string;
}) {
  return (
    <button
      type="button"
      onClick={onClick}
      disabled={disabled}
      title={title}
      class="ask-inline-flex ask-h-8 ask-min-w-8 ask-items-center ask-justify-center ask-rounded-full ask-border ask-border-border ask-bg-surface ask-px-3 ask-text-[12px] ask-font-medium ask-text-text hover:ask-bg-surfaceElevated disabled:ask-cursor-not-allowed disabled:ask-opacity-50"
    >
      {label}
    </button>
  ) as any;
}

function ZoomPanel({ context }: { context?: AsukaModuleContext } = {}) {
  const t = useT();
  const activeRenderEngineKind = useAsukaStore((s: any) => s.activeRenderEngineKind ?? 'fabric');
  const engine = context?.getEngine?.() ?? null;
  const permissions = useAsukaStore((s: any) => s.permissions ?? DEFAULT_PERMISSIONS);
  const [zoom, setZoom] = useState(1);
  const [inputValue, setInputValue] = useState('100');

  useEffect(() => {
    if (activeRenderEngineKind === '3d') {
      setZoom(1);
      return undefined;
    }
    const sync = () => setZoom(Math.max(0.1, Number(engine?.viewport.getZoom?.() ?? 1) || 1));
    sync();
    return engine?.events.subscribe((event) => {
      if (event.type === 'viewport' || event.type === 'changed' || event.type === 'mutation') sync();
    }) ?? (() => {});
  }, [engine, activeRenderEngineKind]);

  const zoomPct = useMemo(() => clamp(Math.round(zoom * 100), 10, 400), [zoom]);

  useEffect(() => {
    setInputValue(String(zoomPct));
  }, [zoomPct]);

  const applyZoomPct = (pct: number) => {
    const nextPct = clamp(pct, 10, 400);
    if (activeRenderEngineKind === '3d') {
      if (nextPct > zoomPct) engine?.viewport.zoomIn();
      else if (nextPct < zoomPct) engine?.viewport.zoomOut();
      setZoom(nextPct / 100);
      setInputValue(String(nextPct));
      return;
    }
    const next = nextPct / 100;
    engine?.viewport.setZoom?.(next);
    setZoom(next);
    setInputValue(String(nextPct));
  };

  const nudgeZoom = (deltaPct: number) => {
    if (activeRenderEngineKind === '3d') {
      if (deltaPct > 0) engine?.viewport.zoomIn();
      else if (deltaPct < 0) engine?.viewport.zoomOut();
      const nextPct = clamp(zoomPct + deltaPct, 10, 400);
      setZoom(nextPct / 100);
      setInputValue(String(nextPct));
      return;
    }
    applyZoomPct(zoomPct + deltaPct);
  };

  const zoomIn = () => {
    if (!(permissions as any)?.canZoomIn) return;
    nudgeZoom(10);
  };

  const zoomOut = () => {
    if (!(permissions as any)?.canZoomOut) return;
    nudgeZoom(-10);
  };

  const reset = () => {
    if (!(permissions as any)?.canResetViewport) return;
    if (activeRenderEngineKind === '3d') {
      engine?.viewport.fit();
      setZoom(1);
      setInputValue('100');
      return;
    }
    engine?.viewport.reset?.();
    setZoom(1);
  };

  const fit = () => {
    if (!(permissions as any)?.canFitViewport) return;
    if (activeRenderEngineKind === '3d') {
      engine?.viewport.fit();
      setZoom(1);
      setInputValue('100');
      return;
    }
    engine?.viewport.fit();
    setZoom(Math.max(0.1, Number(engine?.viewport.getZoom?.() ?? 1) || 1));
  };

  const commitInput = () => {
    const parsed = Number(inputValue.replace(/[^\d.]/g, ''));
    if (!Number.isFinite(parsed)) {
      setInputValue(String(zoomPct));
      return;
    }
    applyZoomPct(parsed);
  };

  if (!(permissions as any)?.canUseZoom) {
    return <div class="ask-text-[11px] ask-text-textMuted">{t('zoom.notAllowed', 'Zoom tools are not allowed.')}</div> as any;
  }

  return (
    <div class="ask-flex ask-flex-col ask-gap-4 ask-p-4">
      <div class="ask-flex ask-items-center ask-justify-between ask-gap-3">
        <div class="ask-flex ask-items-center ask-gap-2">
          <div class="ask-flex ask-h-10 ask-w-10 ask-items-center ask-justify-center ask-rounded-2xl ask-border ask-border-border ask-bg-surfaceElevated ask-text-textMuted">
            <span class="ask-text-sm">🔍</span>
          </div>
          <div>
            <div class="ask-text-sm ask-font-semibold ask-text-text">{t('zoom.canvasZoom', 'Canvas zoom')}</div>
            <div class="ask-text-[11px] ask-text-textMuted">{t('zoom.adjustViewportHint', 'Adjust the viewport quickly and precisely.')}</div>
          </div>
        </div>
        <div class="ask-rounded-full ask-border ask-border-border ask-bg-surface ask-px-3 ask-py-1 ask-text-sm ask-font-semibold ask-text-text">
          {zoomPct}%
        </div>
      </div>

      <div class="ask-grid ask-grid-cols-2 ask-gap-2">
        <ZoomChip label={t('zoom.minus10', '−10%')} onClick={zoomOut} disabled={!((permissions as any)?.canZoomOut ?? true)} title={t('zoom.zoomOut', 'Zoom out')} />
        <ZoomChip label={t('zoom.plus10', '+10%')} onClick={zoomIn} disabled={!((permissions as any)?.canZoomIn ?? true)} title={t('zoom.zoomIn', 'Zoom in')} />
        <ZoomChip label={t('common.reset', 'Reset')} onClick={reset} disabled={!((permissions as any)?.canResetViewport ?? true)} title={t('zoom.resetView', 'Reset view')} />
        <ZoomChip label={t('zoom.fit', 'Fit')} onClick={fit} disabled={!((permissions as any)?.canFitViewport ?? true)} title={t('zoom.fitToViewport', 'Fit to viewport')} />
      </div>

      <div class="ask-rounded-2xl ask-border ask-border-border ask-bg-surface ask-p-3">
        <div class="ask-mb-2 ask-flex ask-items-center ask-justify-between ask-gap-3 ask-text-[11px] ask-font-medium ask-text-textMuted">
          <span>{t('zoom.zoomLevel', 'Zoom level')}</span>
          <div class="ask-flex ask-items-center ask-gap-1">
            {[25, 50, 100, 200].map((preset) => (
              <button
                key={preset}
                type="button"
                onClick={() => applyZoomPct(preset)}
                class={`ask-rounded-full ask-border ask-px-2 ask-py-0.5 ask-text-[11px] ask-font-medium ${zoomPct === preset ? 'ask-border-primary ask-bg-primary/10 ask-text-primary' : 'ask-border-border ask-bg-surface ask-text-textMuted hover:ask-bg-surfaceElevated'}`}
              >
                {preset}%
              </button>
            ))}
          </div>
        </div>

        <div class="ask-flex ask-items-center ask-gap-3">
          <input
            type="range"
            min={10}
            max={400}
            step={1}
            value={zoomPct}
            onInput={(e) => applyZoomPct(Number((e.currentTarget as HTMLInputElement).value || 100))}
            class="ask-h-2 ask-w-full ask-cursor-pointer"
          />
          <div class="ask-flex ask-items-center ask-gap-1 ask-rounded-xl ask-border ask-border-border ask-bg-surfaceElevated ask-px-2 ask-py-1">
            <input
              type="number"
              min={10}
              max={400}
              step={1}
              value={inputValue}
              onInput={(e) => setInputValue((e.currentTarget as HTMLInputElement).value)}
              onBlur={commitInput}
              onKeyDown={(e) => {
                if (e.key === 'Enter') {
                  e.preventDefault();
                  commitInput();
                }
              }}
              class="ask-theme-input ask-h-8 ask-w-14 ask-border-0 ask-bg-transparent ask-px-1 ask-text-right ask-text-sm ask-font-semibold ask-text-text ask-shadow-none focus:ask-outline-none"
            />
            <span class="ask-text-xs ask-font-medium ask-text-textMuted">%</span>
          </div>
        </div>
      </div>
    </div>
  );
}

registerModule({
  id: 'zoom',
  label: 'Zoom',
  icon: 'zoom',
  displayMode: 'floating',
  supportedEngines: ['fabric', '3d'],
  render: (props: any) => <ZoomPanel context={props?.context} />,
});

export default ZoomPanel;
