import { useCallback, useEffect, useMemo, useState } from 'preact/hooks';
import type { FunctionalComponent } from 'preact';
import { registerModule, type AsukaModuleComponent } from '../../core/modules';
import { useT } from '../../hooks/useT';
import { useAsukaStore } from '../../store/asukaStore';
import { DEFAULT_PERMISSIONS } from '../../core/permissions';
import { Card, CardContent } from '../core/Card';
import { Field } from '../core/Field';
import { Input } from '../core/Input';
import { Button } from '../core/Button';
import { onAsukaEvent } from '../../core/events';
import {
  getRenderEngineObjectTransform,
  getRenderEngineSelectedObjects,
  measureRenderEngineLayer,
  updateRenderEngineObjectTransform,
  type AsukaObjectSummary,
  type AsukaObjectTransform,
} from '../../core/renderEngine';
import type { AsukaLayerMeasurement } from '../../core/physicalMeasurement';
import { getObjectFixedSlotConstraints } from '../../core/fixedSlotConstraints';

function fmt(n: any): string {
  const num = Number(n ?? 0);
  if (!Number.isFinite(num)) return '0';
  return Number.isInteger(num) ? String(num) : String(Math.round(num * 100) / 100);
}

function parseMaybeNumber(v: string): number | null {
  const n = Number(v);
  return Number.isFinite(n) ? n : null;
}

function isMultiSelection(obj: AsukaObjectSummary | null): boolean {
  const kind = String(obj?.kind ?? '').toLowerCase();
  return kind === 'activeselection' || kind === 'active-selection';
}

function syncTransformState(
  transform: AsukaObjectTransform | null,
  setX: (v: string) => void,
  setY: (v: string) => void,
  setAngle: (v: string) => void,
) {
  setX(fmt(transform?.x ?? 0));
  setY(fmt(transform?.y ?? 0));
  const rotation = Number(transform?.rotation ?? transform?.rotationZ ?? 0) || 0;
  const isRadians = String(transform?.metadata?.engine ?? '').toLowerCase() === 'babylon';
  setAngle(fmt(isRadians ? (rotation * 180) / Math.PI : rotation));
}

const TransformModule: AsukaModuleComponent = ({ context }) => {
  const t = useT();
  const permissions = useAsukaStore((s: any) => s.permissions ?? DEFAULT_PERMISSIONS);
  const activeViewId = useAsukaStore((s: any) => String(s.activeViewId ?? ''));
  const views = useAsukaStore((s: any) => s.views ?? []);
  const [activeObject, setActiveObject] = useState<AsukaObjectSummary | null>(null);
  const [transform, setTransform] = useState<AsukaObjectTransform | null>(null);
  const [measurement, setMeasurement] = useState<AsukaLayerMeasurement | null>(null);
  const [x, setX] = useState('0');
  const [y, setY] = useState('0');
  const [angle, setAngle] = useState('0');

  const engine = context.getEngine?.() ?? null;
  const activeView = useMemo(
    () => views.find((view: any) => String(view?.id ?? '') === activeViewId) ?? null,
    [activeViewId, views],
  );

  const sync = useCallback(() => {
    const selected = getRenderEngineSelectedObjects(engine)[0] ?? null;
    setActiveObject(selected);
    if (!selected || isMultiSelection(selected)) {
      setTransform(null);
      setMeasurement(null);
      return;
    }
    const nextTransform = getRenderEngineObjectTransform(engine, selected.id);
    setTransform(nextTransform);
    setMeasurement(measureRenderEngineLayer(engine, selected.id, {
      physicalSize: activeView?.physicalSize ?? null,
      physicalSource: 'view',
      variationId: activeView?.physicalSize?.activeVariantId ?? null,
    }));
    syncTransformState(nextTransform, setX, setY, setAngle);
  }, [activeView, engine]);

  useEffect(() => {
    sync();
    const off1 = onAsukaEvent('renderEngine.changed', sync);
    const off2 = engine?.events.subscribe((event) => {
      if (event.type !== 'viewport') sync();
    });
    return () => { off1?.(); off2?.(); };
  }, [engine, sync]);

  const objectId = useMemo(() => String(activeObject?.id ?? ''), [activeObject]);
  const is3DSelection = String(transform?.metadata?.engine ?? activeObject?.metadata?.engine ?? '').toLowerCase() === 'babylon';
  const canMove = (permissions as any)?.canMoveObject !== false;
  const canRotate = (permissions as any)?.canRotateObject !== false;
  const fixedSlotConstraints = getObjectFixedSlotConstraints(
    activeObject?.metadata?.fixedSlotConstraints ? activeObject : transform,
  );
  const canMoveX = canMove && !fixedSlotConstraints.lockX;
  const canMoveY = canMove && !fixedSlotConstraints.lockY;
  const canRotateObject = canRotate && !fixedSlotConstraints.lockRotation;
  const physical = measurement?.physical;
  const imageResolution = measurement?.image;

  const commitPatch = (patch: Record<string, any>) => {
    if (!activeObject || !objectId || activeObject.locked) return;
    updateRenderEngineObjectTransform(engine, objectId, patch as any, { source: 'transform-module' });
    const fresh = getRenderEngineObjectTransform(engine, objectId) ?? transform;
    setTransform(fresh);
    syncTransformState(fresh, setX, setY, setAngle);
  };

  const nudge = (dx = 0, dy = 0) => {
    if (!activeObject || !canMove) return;
    const nextDx = canMoveX ? dx : 0;
    const nextDy = canMoveY ? dy : 0;
    if (!nextDx && !nextDy) return;
    if (is3DSelection) {
      const step = 0.08;
      commitPatch({ deltaX: nextDx * step, deltaY: -nextDy * step });
      return;
    }
    commitPatch({ deltaX: nextDx, deltaY: nextDy });
  };

  const rotateBy = (delta = 0) => {
    if (!activeObject || !canRotateObject) return;
    commitPatch(is3DSelection ? { rotationBy: (delta * Math.PI) / 180 } : { rotationBy: delta });
  };

  if (!engine || !activeObject) {
    return (
      <Card>
        <CardContent>
          <p className="ask-text-[12px] ask-leading-5 ask-text-textMuted">{t('transform.empty', 'Select an object to edit its position and rotation.')}</p>
        </CardContent>
      </Card>
    );
  }

  if (isMultiSelection(activeObject)) {
    return (
      <Card>
        <CardContent>
          <p className="ask-text-[12px] ask-leading-5 ask-text-textMuted">{t('transform.singleObjectOnly', 'Select a single object to edit its exact position and rotation.')}</p>
        </CardContent>
      </Card>
    );
  }

  return (
    <Card>
      <CardContent className="ask-flex ask-flex-col ask-gap-3">
        <div className="ask-rounded-2xl ask-border ask-border-borderSubtle ask-bg-surfaceAlt ask-p-3">
          <div className="ask-mb-2 ask-flex ask-items-center ask-justify-between ask-gap-2">
            <span className="ask-text-[12px] ask-font-semibold ask-text-text">{t('transform.position', 'Position')}</span>
            <span className="ask-text-[10px] ask-text-textMuted">{objectId ? `#${objectId.slice(0, 6)}` : t('transform.selectedObject', 'Selected')}</span>
          </div>

          <div className="ask-grid ask-grid-cols-2 ask-gap-2">
            <Field label={t('transform.x', 'X')}>
              <Input
                type="number"
                value={x}
                disabled={!canMoveX || activeObject.locked}
                className="ask-h-9"
                onInput={(e: any) => setX(String(e.currentTarget?.value ?? ''))}
                onBlur={() => {
                  const next = parseMaybeNumber(x);
                  if (next == null) return syncTransformState(transform, setX, setY, setAngle);
                  if (canMoveX) commitPatch({ x: next });
                }}
              />
            </Field>
            <Field label={t('transform.y', 'Y')}>
              <Input
                type="number"
                value={y}
                disabled={!canMoveY || activeObject.locked}
                className="ask-h-9"
                onInput={(e: any) => setY(String(e.currentTarget?.value ?? ''))}
                onBlur={() => {
                  const next = parseMaybeNumber(y);
                  if (next == null) return syncTransformState(transform, setX, setY, setAngle);
                  if (canMoveY) commitPatch({ y: next });
                }}
              />
            </Field>
          </div>

          <div className="ask-mt-2 ask-grid ask-grid-cols-4 ask-gap-2">
            <Button size="sm" variant="secondary" disabled={!canMoveX || activeObject.locked} onClick={() => nudge(-1, 0)}>{t('transform.leftShort', '←')}</Button>
            <Button size="sm" variant="secondary" disabled={!canMoveY || activeObject.locked} onClick={() => nudge(0, -1)}>{t('transform.upShort', '↑')}</Button>
            <Button size="sm" variant="secondary" disabled={!canMoveY || activeObject.locked} onClick={() => nudge(0, 1)}>{t('transform.downShort', '↓')}</Button>
            <Button size="sm" variant="secondary" disabled={!canMoveX || activeObject.locked} onClick={() => nudge(1, 0)}>{t('transform.rightShort', '→')}</Button>
          </div>
        </div>

        <div className="ask-rounded-2xl ask-border ask-border-borderSubtle ask-bg-surfaceAlt ask-p-3">
          <div className="ask-flex ask-items-center ask-justify-between ask-gap-2">
            <span className="ask-text-[12px] ask-font-semibold ask-text-text">{t('transform.dimensions', 'Dimensions')}</span>
            {physical?.calibrated ? (
              <span className="ask-text-[10px] ask-font-medium ask-text-textMuted">{physical.source === 'print-area' ? t('transform.printAreaCalibration', 'Drawing area') : t('transform.viewCalibration', 'View')}</span>
            ) : (
              <span className="ask-text-[10px] ask-text-textMuted">{t('transform.notCalibrated', 'Not calibrated')}</span>
            )}
          </div>

          {physical?.calibrated && physical.width != null && physical.height != null ? (
            <div className="ask-mt-2">
              <div className="ask-text-[20px] ask-font-semibold ask-text-textMain">
                {fmt(physical.width)} x {fmt(physical.height)} {physical.unit}
              </div>
              <div className="ask-mt-1 ask-text-[11px] ask-text-textMuted">
                {fmt(physical.area)} {physical.areaUnit} | {fmt(measurement?.relative.width)}% x {fmt(measurement?.relative.height)}%
                {physical.variationId ? ` | ${physical.variationId}` : ''}
              </div>
            </div>
          ) : (
            <div className="ask-mt-2 ask-text-[12px] ask-text-textMain">
              {fmt(measurement?.native.width)} x {fmt(measurement?.native.height)} {measurement?.native.unit}
            </div>
          )}

          {measurement?.constraints.fits === false ? (
            <div role="alert" className="ask-mt-2 ask-rounded-md ask-border ask-border-danger/30 ask-bg-danger/5 ask-px-2 ask-py-1.5 ask-text-[10px] ask-text-danger">
              {t('transform.outsidePhysicalLimits', 'This layer exceeds the physical print limits.')} {measurement.constraints.maxWidth} x {measurement.constraints.maxHeight} {measurement.constraints.unit}
            </div>
          ) : null}

          {imageResolution ? (
            <div className={`ask-mt-2 ask-rounded-md ask-border ask-px-2 ask-py-1.5 ask-text-[10px] ${imageResolution.sufficient === false ? 'ask-border-danger/30 ask-bg-danger/5 ask-text-danger' : 'ask-border-borderSubtle ask-bg-surface ask-text-textMuted'}`}>
              {imageResolution.vector
                ? t('transform.vectorResolution', 'Vector image: resolution independent')
                : imageResolution.effectiveDpiX != null && imageResolution.effectiveDpiY != null
                  ? `${Math.round(Math.min(imageResolution.effectiveDpiX, imageResolution.effectiveDpiY))} DPI | ${Math.round(imageResolution.pixelWidth ?? 0)} x ${Math.round(imageResolution.pixelHeight ?? 0)} px | ${imageResolution.minimumDpi} DPI minimum`
                  : t('transform.resolutionUnknown', 'Source resolution unavailable until the area is calibrated.')}
            </div>
          ) : null}
        </div>

        <div className="ask-rounded-2xl ask-border ask-border-borderSubtle ask-bg-surfaceAlt ask-p-3">
          <div className="ask-mb-2 ask-flex ask-items-center ask-justify-between ask-gap-2">
            <span className="ask-text-[12px] ask-font-semibold ask-text-text">{t('transform.rotation', 'Rotation')}</span>
            <span className="ask-text-[10px] ask-text-textMuted">{angle}°</span>
          </div>

          <div className="ask-grid ask-grid-cols-[1fr_auto_auto] ask-gap-2">
            <Input
              type="number"
              value={angle}
              disabled={!canRotateObject || activeObject.locked}
              className="ask-h-9"
              onInput={(e: any) => setAngle(String(e.currentTarget?.value ?? ''))}
              onBlur={() => {
                const next = parseMaybeNumber(angle);
                if (next == null) return syncTransformState(transform, setX, setY, setAngle);
                if (canRotateObject) commitPatch(is3DSelection ? { rotation: (next * Math.PI) / 180 } : { rotation: next });
              }}
            />
            <Button size="sm" variant="secondary" disabled={!canRotateObject || activeObject.locked} onClick={() => rotateBy(-15)}>-15°</Button>
            <Button size="sm" variant="secondary" disabled={!canRotateObject || activeObject.locked} onClick={() => rotateBy(15)}>+15°</Button>
          </div>
        </div>
      </CardContent>
    </Card>
  );
};

registerModule({
  id: 'transform',
  label: 'Transform',
  icon: 'transform',
  displayMode: 'floating',
  supportedEngines: ['fabric', '3d'],
  requiredCapabilities: ['transform'],
  keepMounted: true,
  render: TransformModule as AsukaModuleComponent,
});

export default TransformModule as FunctionalComponent;
