import type { FunctionalComponent } from 'preact';
import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks';

import { registerModule, type AsukaModuleComponent } from '../../core/modules';
import { listVariationCameraTargets } from '../../core/camera';
import type { AsukaCameraLimits, AsukaCameraView } from '../../types/camera';
import { useAsukaStore } from '../../store/asukaStore';
import { useT } from '../../hooks/useT';
import { Button } from '../core/Button';
import { Card, CardContent } from '../core/Card';
import { Field } from '../core/Field';
import { Input } from '../core/Input';
import { Select } from '../core/Select';
import { SegmentedControl } from '../core/SegmentedControl';

const numberValue = (value: unknown, fallback = 0): number => {
  const next = Number(value);
  return Number.isFinite(next) ? next : fallback;
};

const format = (value: unknown): string => {
  const next = numberValue(value);
  return String(Math.round(next * 100) / 100);
};

const normalizeTurn = (value: unknown, fallback = 90): number => {
  const normalized = ((numberValue(value, fallback) % 360) + 360) % 360;
  return Math.abs(normalized - 360) < 0.001 ? 0 : normalized;
};

const flattenElements = (items: any[]): any[] => {
  const out: any[] = [];
  const walk = (list: any[]) => {
    for (const element of Array.isArray(list) ? list : []) {
      if (!element || typeof element !== 'object') continue;
      out.push(element);
      walk(Array.isArray(element.children) ? element.children : []);
    }
  };
  walk(items);
  return out;
};

const CameraModule: AsukaModuleComponent = ({ context }) => {
  const t = useT();
  const engine = context.getEngine?.() as any;
  const elements = useAsukaStore((state: any) => Array.isArray(state.elements) ? state.elements : []);
  const activeViewId = useAsukaStore((state: any) => String(state.activeViewId ?? ''));
  const setElementVariationCamera = useAsukaStore((state: any) => state.setElementVariationCamera);
  const permissions = useAsukaStore((state: any) => state.permissions ?? {});
  const canEditPresets = permissions?.canEditElements !== false && permissions?.canEditElementVariations !== false;

  const productElements = useMemo(
    () => flattenElements(elements).filter((element: any) => (String(element?.type ?? '') === 'product-3d' || element?.product3d) && Array.isArray(element?.variations) && element.variations.length),
    [elements],
  );

  const [elementId, setElementId] = useState('');
  const selectedElement = useMemo(
    () => productElements.find((element: any) => String(element?.id ?? '') === elementId) ?? productElements[0] ?? null,
    [productElements, elementId],
  );
  const variationTargets = useMemo(() => listVariationCameraTargets(selectedElement), [selectedElement]);
  const [variationId, setVariationId] = useState('');
  const selectedVariation = useMemo(
    () => variationTargets.find((entry) => entry.id === variationId) ?? variationTargets[0] ?? null,
    [variationTargets, variationId],
  );

  const [turn, setTurn] = useState('90');
  const [tilt, setTilt] = useState('20');
  const [distance, setDistance] = useState('4');
  const [targetX, setTargetX] = useState('0');
  const [targetY, setTargetY] = useState('0');
  const [targetZ, setTargetZ] = useState('0');
  const [fov, setFov] = useState('45');
  const [minTilt, setMinTilt] = useState('2');
  const [maxTilt, setMaxTilt] = useState('85');
  const [maxTurn, setMaxTurn] = useState('180');
  const [turnCenter, setTurnCenter] = useState('90');
  const [quickView, setQuickView] = useState('front');
  const quickViewReferenceRef = useRef<{ turn: number; tilt: number } | null>(null);

  useEffect(() => {
    quickViewReferenceRef.current = null;
  }, [activeViewId]);

  useEffect(() => {
    if (!elementId && productElements[0]) setElementId(String(productElements[0]?.id ?? ''));
  }, [elementId, productElements]);

  useEffect(() => {
    if (!variationTargets.some((entry) => entry.id === variationId)) {
      setVariationId(variationTargets[0]?.id ?? '');
    }
  }, [variationTargets, variationId]);

  const sync = useCallback(() => {
    const camera = engine?.viewport.getCamera?.();
    const limits = engine?.viewport.getCameraLimits?.();
    if (camera) {
      setTurn(format(normalizeTurn(camera.turn)));
      setTilt(format(camera.tilt));
      setDistance(format(camera.distance));
      setTargetX(format(camera.target?.[0]));
      setTargetY(format(camera.target?.[1]));
      setTargetZ(format(camera.target?.[2]));
      setFov(format(camera.fov ?? 45));
      const currentTurn = normalizeTurn(camera.turn);
      const currentTilt = numberValue(camera.tilt, 20);
      if (!quickViewReferenceRef.current) {
        quickViewReferenceRef.current = { turn: currentTurn, tilt: currentTilt };
      }
      const referenceTurn = quickViewReferenceRef.current.turn;
      const normalizedTurn = (((currentTurn - referenceTurn) % 360) + 360) % 360;
      if (numberValue(camera.tilt, 20) >= 80) setQuickView('top');
      else if (normalizedTurn >= 315 || normalizedTurn < 45) setQuickView('front');
      else if (normalizedTurn < 135) setQuickView('left');
      else if (normalizedTurn < 225) setQuickView('back');
      else setQuickView('right');
    }
    if (limits) {
      setMinTilt(format(limits.minTilt));
      setMaxTilt(format(limits.maxTilt));
      setMaxTurn(format(limits.maxTurn));
      setTurnCenter(format(limits.turnCenter));
    }
  }, [engine]);

  useEffect(() => {
    sync();
    const off = engine?.events.subscribe((event: any) => {
      if (event.type === 'viewport' || event.type === 'ready') sync();
    });
    return () => off?.();
  }, [engine, sync]);

  const readFormView = (): AsukaCameraView => ({
    turn: numberValue(turn, 90),
    tilt: numberValue(tilt, 20),
    distance: Math.max(0.01, numberValue(distance, 4)),
    target: [numberValue(targetX), numberValue(targetY), numberValue(targetZ)],
    fov: numberValue(fov, 45),
  });

  const applyView = () => {
    engine?.viewport.setCamera?.(readFormView(), { animate: true, duration: 420, source: 'camera.module' });
  };

  const applyQuickView = (preset: string) => {
    const current = engine?.viewport.getCamera?.();
    if (!current) return;
    const reference = quickViewReferenceRef.current ?? {
      turn: numberValue(current.turn, 90),
      tilt: numberValue(current.tilt, 20),
    };
    quickViewReferenceRef.current = reference;
    const sideTilt = Math.min(35, Math.max(numberValue(minTilt, 2), reference.tilt));
    const rotations: Record<string, { turn: number; tilt?: number }> = {
      front: { turn: reference.turn, tilt: sideTilt },
      left: { turn: reference.turn + 90, tilt: sideTilt },
      back: { turn: reference.turn + 180, tilt: sideTilt },
      right: { turn: reference.turn - 90, tilt: sideTilt },
      top: { turn: reference.turn, tilt: Math.min(numberValue(maxTilt, 85), 85) },
    };
    const next = rotations[preset];
    if (!next) return;
    setQuickView(preset);
    engine?.viewport.setCamera?.({
      ...current,
      turn: next.turn,
      tilt: next.tilt ?? sideTilt,
    }, { animate: true, duration: 360, source: `camera.preset.${preset}` });
  };

  const applyLimits = () => {
    const limits: Partial<AsukaCameraLimits> = {
      minTilt: numberValue(minTilt, 2),
      maxTilt: numberValue(maxTilt, 85),
      maxTurn: numberValue(maxTurn, 180),
      turnCenter: numberValue(turnCenter, 90),
    };
    engine?.viewport.setCameraLimits?.(limits);
    sync();
  };

  const saveVariationView = () => {
    if (!selectedElement || !selectedVariation || !canEditPresets) return;
    const camera = engine?.viewport.getCamera?.();
    if (!camera) return;
    setElementVariationCamera?.(String(selectedElement.id), selectedVariation.id, camera);
  };

  const previewVariationView = () => {
    if (!selectedVariation?.camera) return;
    engine?.viewport.setCamera?.(selectedVariation.camera, { animate: true, duration: 520, source: 'camera.module.preview' });
  };

  const clearVariationView = () => {
    if (!selectedElement || !selectedVariation || !canEditPresets) return;
    setElementVariationCamera?.(String(selectedElement.id), selectedVariation.id, null);
  };

  if (!engine?.viewport.getCamera) {
    return <div className="ask-p-4 ask-text-[12px] ask-text-textMuted">{t('camera.only3d', 'Camera settings are available in 3D views.')}</div>;
  }

  const numericField = (label: string, value: string, setter: (value: string) => void) => (
    <Field label={label}>
      <Input type="number" value={value} onInput={(event: any) => setter(String(event.currentTarget?.value ?? ''))} />
    </Field>
  );

  return (
    <div className="ask-flex ask-flex-col ask-gap-3 ask-p-3">
      <Card>
        <CardContent className="ask-flex ask-flex-col ask-gap-3">
          <div>
            <div className="ask-text-[13px] ask-font-semibold ask-text-text">{t('camera.quickViews', 'Quick views')}</div>
            <div className="ask-text-[11px] ask-text-textMuted">{t('camera.quickViewsHint', 'Change orientation without moving the product or changing the zoom.')}</div>
          </div>
          <SegmentedControl
            value={quickView}
            options={[
              { value: 'front', label: t('camera.front', 'Front') },
              { value: 'left', label: t('camera.left', 'Left') },
              { value: 'back', label: t('camera.back', 'Back') },
              { value: 'right', label: t('camera.right', 'Right') },
              { value: 'top', label: t('camera.top', 'Top') },
            ]}
            onChange={applyQuickView}
            className="ask-w-full ask-overflow-x-auto"
          />
          <Button size="sm" variant="secondary" onClick={() => engine?.viewport.fit?.()}>
            {t('camera.fitProduct', 'Fit product')}
          </Button>
        </CardContent>
      </Card>

      <Card>
        <CardContent className="ask-flex ask-flex-col ask-gap-3">
          <div>
            <div className="ask-text-[13px] ask-font-semibold ask-text-text">{t('camera.limits', 'Camera limits')}</div>
            <div className="ask-text-[11px] ask-text-textMuted">{t('camera.limitsHint', 'Restrict vertical tilt and horizontal rotation for this view.')}</div>
          </div>
          <div className="ask-grid ask-grid-cols-2 ask-gap-2">
            {numericField(t('camera.minTilt', 'Minimum tilt (°)'), minTilt, setMinTilt)}
            {numericField(t('camera.maxTilt', 'Maximum tilt (°)'), maxTilt, setMaxTilt)}
            {numericField(t('camera.maxTurn', 'Maximum rotation (°)'), maxTurn, setMaxTurn)}
            {numericField(t('camera.turnCenter', 'Rotation center (°)'), turnCenter, setTurnCenter)}
          </div>
          <div className="ask-flex ask-flex-wrap ask-gap-2">
            <Button size="sm" variant="secondary" onClick={() => setTurnCenter(turn)}>{t('camera.useCurrentCenter', 'Use current rotation')}</Button>
            <Button size="sm" variant="primary" onClick={applyLimits}>{t('common.apply', 'Apply')}</Button>
          </div>
        </CardContent>
      </Card>

      <Card>
        <CardContent className="ask-flex ask-flex-col ask-gap-3">
          <div>
            <div className="ask-text-[13px] ask-font-semibold ask-text-text">{t('camera.currentView', 'Current camera')}</div>
            <div className="ask-text-[11px] ask-text-textMuted">{t('camera.currentViewHint', 'Angles are in degrees and the target uses 3D world coordinates.')}</div>
          </div>
          <div className="ask-grid ask-grid-cols-2 ask-gap-2">
            {numericField(t('camera.turn', 'Rotation (°)'), turn, setTurn)}
            {numericField(t('camera.tilt', 'Tilt (°)'), tilt, setTilt)}
            {numericField(t('camera.distance', 'Distance'), distance, setDistance)}
            {numericField(t('camera.fov', 'Field of view (°)'), fov, setFov)}
            {numericField(t('camera.targetX', 'Target X'), targetX, setTargetX)}
            {numericField(t('camera.targetY', 'Target Y'), targetY, setTargetY)}
            {numericField(t('camera.targetZ', 'Target Z'), targetZ, setTargetZ)}
          </div>
          <div className="ask-flex ask-gap-2">
            <Button size="sm" variant="secondary" onClick={sync}>{t('common.refresh', 'Refresh')}</Button>
            <Button size="sm" variant="primary" onClick={applyView}>{t('camera.move', 'Move camera')}</Button>
          </div>
        </CardContent>
      </Card>

      <Card>
        <CardContent className="ask-flex ask-flex-col ask-gap-3">
          <div>
            <div className="ask-text-[13px] ask-font-semibold ask-text-text">{t('camera.variationViews', 'Variation camera views')}</div>
            <div className="ask-text-[11px] ask-text-textMuted">{t('camera.variationViewsHint', 'Capture the current camera for a variation. It will be restored from Elements and Variations.')}</div>
          </div>
          <Field label={t('camera.element', 'Element')}>
            <Select
              value={String(selectedElement?.id ?? '')}
              options={productElements.map((element: any) => ({ value: String(element.id), label: String(element.name ?? element.id) }))}
              onChange={(event: any) => setElementId(String(event.currentTarget?.value ?? ''))}
            />
          </Field>
          <Field label={t('camera.variation', 'Variation')}>
            <Select
              value={selectedVariation?.id ?? ''}
              options={variationTargets.map((entry) => ({ value: entry.id, label: `${entry.camera ? '● ' : ''}${entry.label}` }))}
              onChange={(event: any) => setVariationId(String(event.currentTarget?.value ?? ''))}
            />
          </Field>
          <div className="ask-rounded-xl ask-border ask-border-borderSubtle ask-bg-surfaceAlt ask-p-3 ask-text-[11px] ask-text-textMuted">
            {selectedVariation?.camera
              ? t('camera.saved', 'A camera view is saved for this variation.')
              : t('camera.notSaved', 'No camera view is saved for this variation.')}
          </div>
          <div className="ask-flex ask-flex-wrap ask-gap-2">
            <Button size="sm" variant="primary" disabled={!selectedVariation || !canEditPresets} onClick={saveVariationView}>{t('camera.saveCurrent', 'Save current view')}</Button>
            <Button size="sm" variant="secondary" disabled={!selectedVariation?.camera} onClick={previewVariationView}>{t('camera.preview', 'Preview')}</Button>
            <Button size="sm" variant="danger" disabled={!selectedVariation?.camera || !canEditPresets} onClick={clearVariationView}>{t('common.remove', 'Remove')}</Button>
          </div>
        </CardContent>
      </Card>
    </div>
  );
};

registerModule({
  id: 'camera',
  label: 'Camera',
  icon: 'camera',
  displayMode: 'floating',
  keepMounted: true,
  supportedEngines: ['3d'],
  requiredCapabilities: ['scene3d'],
  render: CameraModule,
});

export default CameraModule as FunctionalComponent;
