import { useEffect, useMemo, useState } from "preact/hooks";
import { registerModule, type AsukaModuleComponent } from "../../core/modules";
import { useT } from "../../hooks/useT";
import { useAsukaStore } from "../../store/asukaStore";
import {
  getRenderEngineObject,
  listRenderEngineObjects,
  type AsukaObjectSummary,
} from "../../core/renderEngine";
import { DEFAULT_PERMISSIONS } from "../../core/permissions";

import { Card, CardHeader, CardTitle, CardContent } from "../core/Card";
import { IconButton } from "../core/IconButton";
import { List, type ListSortEventDetail } from "../core/List";
import { emitAsukaEvent, onAsukaEvent } from "../../core/events";
import { AsukaIcon } from "../core/AsukaIcon";
import { Tooltip } from "../core/Tooltip";
import { ModuleUnavailableState } from "./ModuleUnavailableState";
import {
  getObjectFixedSlotConstraints,
  type AsukaFixedSlotConstraintsLike,
} from "../../core/fixedSlotConstraints";
import {
  getAsukaLayerCategory,
  getAsukaLayerTypeLabel,
  isAsukaLayerObject,
  type AsukaLayerCategory,
} from "../../core/layerClassification";

interface LayerItem {
  id: string;
  identifier: string;
  label: string;
  type: string;
  isActive: boolean;
  visible: boolean;
  locked: boolean;
  readonly?: boolean;
  printAreaId?: string | null;
  printAreaLabel?: string | null;
  fixedSlotConstraints?: AsukaFixedSlotConstraintsLike;
}

const svgIcon = (paths: string, viewBox = "0 0 20 20") =>
  `svg:<svg xmlns="http://www.w3.org/2000/svg" viewBox="${viewBox}" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">${paths}</svg>`;

const DRAG_ICON = "grab";
const EYE_ICON = "eye";
const EYE_OFF_ICON = "eye-off";
const MOVE_UP_ICON = svgIcon('<path d="M10 16V4"/><path d="M5 9l5-5 5 5"/>');
const MOVE_DOWN_ICON = svgIcon('<path d="M10 4v12"/><path d="M5 11l5 5 5-5"/>');
const MOVE_LEFT_ICON = svgIcon('<path d="M16 10H4"/><path d="M9 5l-5 5 5 5"/>');
const MOVE_RIGHT_ICON = svgIcon(
  '<path d="M4 10h12"/><path d="M11 5l5 5-5 5"/>',
);
const ROTATE_LEFT_ICON = "rotate-left";
const ROTATE_RIGHT_ICON = "rotate-right";
const SCALE_UP_ICON = svgIcon('<path d="M4 16l12-12"/><path d="M9 4h7v7"/>');
const SCALE_DOWN_ICON = svgIcon('<path d="M16 4L4 16"/><path d="M11 16H4V9"/>');

const getLayerIdentifier = (
  obj: any,
  index: number,
  type: AsukaLayerCategory,
): string => {
  const rawId = String(obj?._asukaObjectId ?? obj?.id ?? `layer_${index + 1}`);
  const clean = rawId.replace(/[^a-zA-Z0-9]/g, "");
  const suffix = (clean.slice(-6) || String(index + 1)).toUpperCase();
  const prefix = type === "text"
      ? "TXT"
      : type === "image"
        ? "IMG"
        : type === "design"
          ? "DSN"
          : type === "shape"
            ? "SHP"
            : type === "group"
            ? "GRP"
            : "OBJ";
  return `${prefix}-${suffix}`;
};

const layerFromObjectSummary = (
  obj: AsukaObjectSummary,
  index: number,
): LayerItem => {
  const kind = String(obj?.kind ?? "object");
  const category = getAsukaLayerCategory(obj) ?? "object";
  const shapeKind = String((obj as any)?.metadata?.shapeKind ?? "").trim();
  const printAreaId = String(
    (obj as any)?.targetPrintAreaId ??
      (obj as any)?.parentId ??
      (obj as any)?.metadata?.targetPrintAreaId ??
      (obj as any)?.metadata?.printAreaId ??
      "",
  ).trim();
  const printAreaLabel = String(
    (obj as any)?.printAreaLabel ??
      (obj as any)?.metadata?.printAreaLabel ??
      printAreaId,
  ).trim();
  return {
    id: String(obj?.id ?? `object-${index + 1}`),
    identifier: getLayerIdentifier(
      { _asukaObjectId: obj?.id, type: kind },
      index,
      category,
    ),
    label: String(
      obj?.label ??
        (obj as any)?.metadata?.source?.text ??
        `${shapeKind || getAsukaLayerTypeLabel(category)} ${index + 1}`,
    ),
    type: category,
    isActive: !!obj?.selected,
    visible: obj?.visible !== false,
    locked: !!obj?.locked,
    readonly: !!(obj as any)?.readonly,
    printAreaId: printAreaId || null,
    printAreaLabel: printAreaLabel || null,
    fixedSlotConstraints: getObjectFixedSlotConstraints(obj),
  };
};

const LayersModule: AsukaModuleComponent = ({ context }) => {
  const permissions = useAsukaStore(
    (s: any) => s.permissions ?? DEFAULT_PERMISSIONS,
  );
  const t = useT();
  const engine = context.getEngine?.() ?? null;
  const activeRenderEngineKind = useAsukaStore(
    (s: any) => s.activeRenderEngineKind ?? "fabric",
  );
  const activeViewId = useAsukaStore((s: any) => s.activeViewId);
  const [engineTick, setEngineTick] = useState(0);

  useEffect(() => {
    const bump = () => setEngineTick((v) => v + 1);
    const off1 = engine?.events.subscribe((event) => {
      if (event.type === 'viewport' || /camera|viewport|hover/i.test(String(event.action ?? ''))) return;
      bump();
    });
    const off2 = onAsukaEvent('after:state.restore', bump);
    return () => {
      off1?.();
      off2?.();
    };
  }, [engine, activeRenderEngineKind]);

  const layers = useMemo<LayerItem[]>(() => {
    void engineTick;
    return listRenderEngineObjects(engine, {
      source: "layers",
      includeReadonly: false,
      includeTechnical: false,
      includeScreenBox: false,
    })
      .filter(isAsukaLayerObject)
      .map(layerFromObjectSummary);
  }, [engine, engineTick]);

  const engineCapabilities = useMemo(() => {
    void engineTick;
    return engine?.capabilities ?? {};
  }, [engine, engineTick]);

  const reversed = useMemo(() => layers.slice().reverse(), [layers]);

  const applyDocumentOrderByLayerIds = async (displayOrderedIds: string[]) => {
    const ids = displayOrderedIds
      .map((id) => String(id ?? "").trim())
      .filter(Boolean);
    if (ids.length === 0 || !engine) return;
    try {
      const result = await engine.objects.reorder(ids, {
        order: "display",
        source: "layers",
      });
      emitAsukaEvent("layers.reordered", {
        orderIds: ids,
        displayOrderIds: ids,
        engine: activeRenderEngineKind,
        handled: result.changed,
      });
    } catch {
      emitAsukaEvent("layers.reordered", {
        orderIds: ids,
        displayOrderIds: ids,
        engine: activeRenderEngineKind,
        handled: false,
      });
    }
  };

  const handleSelect = (layerId: string) => {
    if (!(permissions as any)?.canSelectLayers || !engine) return;
    const layer = layers.find((item) => item.id === layerId);
    if (layer?.readonly) return;
    const resolveObject = () => getRenderEngineObject(engine, layerId);
    try {
      engine.selection.select(layerId);
    } catch {}
    const selectedObject = resolveObject();
    const requestPayload = (
      object: AsukaObjectSummary | null = selectedObject,
    ) => ({
      engine: activeRenderEngineKind,
      id: layerId,
      source: "layers",
      forceOpen: activeRenderEngineKind === "3d",
      object: object ?? undefined,
      kind: object?.kind ?? layer?.type ?? "object",
      label: object?.label ?? layer?.label ?? layerId,
      locked: object?.locked ?? layer?.locked ?? false,
      visible: object?.visible ?? layer?.visible ?? true,
      metadata: object?.metadata ?? undefined,
      screenBox: (object as any)?.metadata?.screenBox ?? undefined,
    });
    emitAsukaEvent("selection.request.menu", requestPayload(selectedObject));
    try {
      window.requestAnimationFrame(() =>
        emitAsukaEvent(
          "selection.request.menu",
          requestPayload(resolveObject()),
        ),
      );
    } catch {}
  };

  const handleToggleVisible = (layerId: string) => {
    if (!(permissions as any)?.canHideLayers || !engine) return;
    const layer = layers.find((item) => item.id === layerId);
    void engine.objects.setVisible(layerId, !(layer?.visible ?? true), { source: 'layers' }).catch(() => {});
  };

  const handleToggleLock = (layerId: string) => {
    if (!(permissions as any)?.canLockLayers || !engine) return;
    const layer = layers.find((item) => item.id === layerId);
    if (layer?.readonly) return;
    void engine.objects.setLocked(layerId, !(layer?.locked ?? false), { source: 'layers' }).catch(() => {});
  };

  const handleDeleteLayer = (layerId: string) => {
    if (!(permissions as any)?.canDeleteObject || !engine) return;
    const layer = layers.find((item) => item.id === layerId);
    if (layer?.locked || layer?.readonly) return;
    void engine.objects.remove(layerId).catch(() => {});
  };

  const handleTransform3DLayer = (
    layerId: string,
    patch: Record<string, unknown>,
  ) => {
    if (activeRenderEngineKind !== "3d" || !engine) return;
    const layer = layers.find((item) => item.id === layerId);
    if (!layer || layer.locked) return;
    void engine.objects.updateTransform(layerId, patch as any, { source: 'layers' }).catch(() => {});
  };

  const canTransform3D = (layer: LayerItem) =>
    activeRenderEngineKind === "3d" &&
    layer.isActive &&
    !layer.locked &&
    Boolean(engine) &&
    ((permissions as any)?.canMoveObject !== false ||
      (permissions as any)?.canResizeObject !== false ||
      (permissions as any)?.canRotateObject !== false);

  const nudge3D = 0.08;
  const rotate3D = Math.PI / 18;

  if (!(permissions as any)?.canViewLayers) {
    return (
      <div class="ask-text-[11px] ask-text-textMuted">
        {t("layers.notAvailable", "Layers are not available.")}
      </div>
    );
  }

  if (!engine || !activeViewId) {
    return <ModuleUnavailableState />;
  }

  return (
    <Card className="ask-flex ask-flex-col ask-h-full ask-min-h-0">
      <CardHeader className="ask-flex ask-items-center ask-justify-between ask-gap-2">
        <CardTitle>{t("modules.layers")}</CardTitle>
        <span class="ask-inline-flex ask-items-center ask-rounded-full ask-border ask-border-borderSubtle ask-bg-surface ask-px-2 ask-py-0.5 ask-text-[10px] ask-font-medium ask-text-textMuted">
          {layers.length} objects
        </span>
      </CardHeader>

      <CardContent
        className="ask-flex ask-flex-col ask-gap-4"
        scrollable
        viewportClassName="ask-h-full ask-pr-1"
      >
        {layers.length === 0 ? (
          <p className="ask-rounded-[14px] ask-border ask-border-dashed ask-border-borderSubtle ask-bg-surface ask-px-3 ask-py-4 ask-text-[11px] ask-text-textMuted">
            {t("ui.no_objects_on_canvas_yet")}
          </p>
        ) : null}

        <List
          className="ask-gap-3"
          listId="layers"
          items={reversed}
          empty={
            <div className="ask-pt-2 ask-text-[11px] ask-leading-5 ask-text-textMuted">
              {t("common.noItems", "No items.")}
            </div>
          }
          getKey={(it: LayerItem) => it.id}
          sortable={{
            enabled:
              Boolean((permissions as any)?.canReorderLayers) &&
              engineCapabilities.reorderObjects === true &&
              Boolean(engine?.objects),
            getId: (it: LayerItem) => it.id,
            onSortEnd: (detail: ListSortEventDetail<LayerItem>) => {
              const nextDisplayOrderIds = detail.nextItems.map((x) => x.id);
              void applyDocumentOrderByLayerIds(nextDisplayOrderIds);
            },
          }}
          renderItem={(layer: LayerItem, _index: number) => {
            const typeLabel = getAsukaLayerTypeLabel(
              layer.type as AsukaLayerCategory,
            );
            return (
              <div
                role="button"
                tabIndex={0}
                class={[
                  // NOTE: the right sidebar can be very narrow. We use flex-wrap + a
                  // minimum width for the text column so the label stays readable
                  // (actions wrap under the label when space is tight).
                  "ask-group ask-flex ask-flex-wrap ask-items-center ask-gap-2 ask-rounded-[14px] ask-border ask-px-2 ask-py-2 ask-transition-all",
                  "ask-border-borderSubtle ask-bg-surface hover:ask-bg-surfaceAlt hover:ask-shadow-sm",
                  layer.isActive
                    ? "ask-border-primary/40 ask-bg-primary/5"
                    : "",
                ].join(" ")}
                onClick={() => handleSelect(layer.id)}
                onKeyDown={(e: any) => {
                  if (e.key === "Enter" || e.key === " ") {
                    e.preventDefault();
                    handleSelect(layer.id);
                  }
                }}
                onMouseEnter={() => {
                  engine.objects.preview(layer.id, {
                    mode: "fade-others",
                    dimOpacity: 0.16,
                  });
                }}
                onMouseLeave={() => {
                  engine.objects.clearPreview();
                }}
                onFocus={() => {
                  engine.objects.preview(layer.id, {
                    mode: "fade-others",
                    dimOpacity: 0.16,
                  });
                }}
                onBlur={() => {
                  engine.objects.clearPreview();
                }}
              >
                <div
                  class="ask-flex ask-h-8 ask-w-7 ask-shrink-0 ask-cursor-grab ask-touch-none ask-select-none ask-items-center ask-justify-center ask-rounded-[10px] ask-border ask-border-borderSubtle ask-bg-surfaceAlt ask-text-textMuted active:ask-cursor-grabbing"
                  title={
                    layer.readonly
                      ? t("layers.productLayer", "Product layer")
                      : t("common.dragToReorder", "Drag to reorder")
                  }
                  aria-label={
                    layer.readonly
                      ? t("layers.productLayer", "Product layer")
                      : t("common.dragToReorder", "Drag to reorder")
                  }
                  data-ask-sort-handle={layer.readonly ? "false" : "true"}
                >
                  <AsukaIcon name={DRAG_ICON} size={12} />
                </div>

                <div class="ask-min-w-[120px] ask-flex-1">
                  <div class="ask-truncate ask-text-[12px] ask-font-semibold ask-leading-[1.2] ask-text-textMain">
                    {layer.label}
                  </div>
                  <div class="ask-mt-0.5 ask-flex ask-items-center ask-gap-1 ask-text-[10px] ask-leading-[1.2] ask-text-textMuted">
                    <span class="ask-truncate">{typeLabel}</span>
                    <span aria-hidden="true">•</span>
                    <span class="ask-shrink-0 ask-font-medium">
                      {layer.identifier}
                    </span>
                    {layer.printAreaLabel ? (
                      <>
                        <span aria-hidden="true">•</span>
                        <span class="ask-truncate" title={layer.printAreaId ?? undefined}>
                          {layer.printAreaLabel}
                        </span>
                      </>
                    ) : null}
                    {layer.isActive ? (
                      <>
                        <span aria-hidden="true">•</span>
                        <span class="ask-truncate ask-text-primary">
                          Selected
                        </span>
                      </>
                    ) : null}
                  </div>
                </div>

                <div class="ask-ml-auto ask-flex ask-items-center ask-justify-end ask-gap-1 ask-shrink-0">
                  <Tooltip
                    content={layer.visible ? "Hide layer" : "Show layer"}
                    direction="top"
                  >
                    <IconButton
                      variant="plain"
                      title={layer.visible ? "Hide" : "Show"}
                      aria-pressed={!layer.visible}
                      onClick={(e) => {
                        e.stopPropagation();
                        handleToggleVisible(layer.id);
                      }}
                      className={[
                        "ask-h-7 ask-w-7 ask-text-textMuted hover:ask-text-textMain",
                        !layer.visible ? "ask-text-textMain" : "",
                      ].join(" ")}
                    >
                      <AsukaIcon
                        name={layer.visible ? EYE_ICON : EYE_OFF_ICON}
                        size={13}
                      />
                    </IconButton>
                  </Tooltip>
                  {!layer.readonly ? (
                    <>
                      <Tooltip
                        content={layer.locked ? "Unlock layer" : "Lock layer"}
                        direction="top"
                      >
                        <IconButton
                          variant="plain"
                          title={layer.locked ? "Unlock" : "Lock"}
                          aria-pressed={layer.locked}
                          onClick={(e) => {
                            e.stopPropagation();
                            handleToggleLock(layer.id);
                          }}
                          className={[
                            "ask-h-7 ask-w-7 ask-text-textMuted hover:ask-text-textMain",
                            layer.locked ? "ask-text-amber-600" : "",
                          ].join(" ")}
                        >
                          <AsukaIcon
                            name={layer.locked ? "lock" : "unlock"}
                            size={13}
                          />
                        </IconButton>
                      </Tooltip>
                      <Tooltip
                        content={t("layers.deleteLayer", "Delete layer")}
                        direction="top"
                      >
                        <IconButton
                          variant="plain"
                          title={t("common.delete", "Delete")}
                          onClick={(e) => {
                            e.stopPropagation();
                            handleDeleteLayer(layer.id);
                          }}
                          className="ask-h-7 ask-w-7 ask-text-textMuted hover:ask-text-danger"
                        >
                          <AsukaIcon name="trash" size={12} />
                        </IconButton>
                      </Tooltip>
                    </>
                  ) : null}
                </div>

                {canTransform3D(layer) ? (
                  <div
                    class="ask-ml-9 ask-mt-1 ask-flex ask-w-full ask-flex-wrap ask-items-center ask-gap-1 ask-rounded-[12px] ask-border ask-border-borderSubtle ask-bg-surfaceAlt ask-p-1"
                    onClick={(e) => e.stopPropagation()}
                  >
                    <span class="ask-px-1 ask-text-[10px] ask-font-medium ask-text-textMuted">
                      3D transform
                    </span>
                    {(permissions as any)?.canMoveObject !== false ? (
                      <>
                        <Tooltip content="Move left" direction="top">
                          <IconButton
                            variant="plain"
                            title="Move left"
                            disabled={layer.fixedSlotConstraints?.lockX === true}
                            className="ask-h-7 ask-w-7"
                            onClick={() =>
                              handleTransform3DLayer(layer.id, {
                                deltaX: -nudge3D,
                              })
                            }
                          >
                            <AsukaIcon name={MOVE_LEFT_ICON} size={12} />
                          </IconButton>
                        </Tooltip>
                        <Tooltip content="Move up" direction="top">
                          <IconButton
                            variant="plain"
                            title="Move up"
                            disabled={layer.fixedSlotConstraints?.lockY === true}
                            className="ask-h-7 ask-w-7"
                            onClick={() =>
                              handleTransform3DLayer(layer.id, {
                                deltaY: nudge3D,
                              })
                            }
                          >
                            <AsukaIcon name={MOVE_UP_ICON} size={12} />
                          </IconButton>
                        </Tooltip>
                        <Tooltip content="Move down" direction="top">
                          <IconButton
                            variant="plain"
                            title="Move down"
                            disabled={layer.fixedSlotConstraints?.lockY === true}
                            className="ask-h-7 ask-w-7"
                            onClick={() =>
                              handleTransform3DLayer(layer.id, {
                                deltaY: -nudge3D,
                              })
                            }
                          >
                            <AsukaIcon name={MOVE_DOWN_ICON} size={12} />
                          </IconButton>
                        </Tooltip>
                        <Tooltip content="Move right" direction="top">
                          <IconButton
                            variant="plain"
                            title="Move right"
                            disabled={layer.fixedSlotConstraints?.lockX === true}
                            className="ask-h-7 ask-w-7"
                            onClick={() =>
                              handleTransform3DLayer(layer.id, {
                                deltaX: nudge3D,
                              })
                            }
                          >
                            <AsukaIcon name={MOVE_RIGHT_ICON} size={12} />
                          </IconButton>
                        </Tooltip>
                      </>
                    ) : null}
                    {(permissions as any)?.canResizeObject !== false ? (
                      <>
                        <Tooltip content="Scale down" direction="top">
                          <IconButton
                            variant="plain"
                            title="Scale down"
                            className="ask-h-7 ask-w-7"
                            onClick={() =>
                              handleTransform3DLayer(layer.id, { scaleBy: 0.9 })
                            }
                          >
                            <AsukaIcon name={SCALE_DOWN_ICON} size={12} />
                          </IconButton>
                        </Tooltip>
                        <Tooltip content="Scale up" direction="top">
                          <IconButton
                            variant="plain"
                            title="Scale up"
                            className="ask-h-7 ask-w-7"
                            onClick={() =>
                              handleTransform3DLayer(layer.id, { scaleBy: 1.1 })
                            }
                          >
                            <AsukaIcon name={SCALE_UP_ICON} size={12} />
                          </IconButton>
                        </Tooltip>
                      </>
                    ) : null}
                    {(permissions as any)?.canRotateObject !== false ? (
                      <>
                        <Tooltip content="Rotate left" direction="top">
                          <IconButton
                            variant="plain"
                            title="Rotate left"
                            disabled={layer.fixedSlotConstraints?.lockRotation === true}
                            className="ask-h-7 ask-w-7"
                            onClick={() =>
                              handleTransform3DLayer(layer.id, {
                                rotationBy: -rotate3D,
                              })
                            }
                          >
                            <AsukaIcon name={ROTATE_LEFT_ICON} size={12} />
                          </IconButton>
                        </Tooltip>
                        <Tooltip content="Rotate right" direction="top">
                          <IconButton
                            variant="plain"
                            title="Rotate right"
                            disabled={layer.fixedSlotConstraints?.lockRotation === true}
                            className="ask-h-7 ask-w-7"
                            onClick={() =>
                              handleTransform3DLayer(layer.id, {
                                rotationBy: rotate3D,
                              })
                            }
                          >
                            <AsukaIcon name={ROTATE_RIGHT_ICON} size={12} />
                          </IconButton>
                        </Tooltip>
                      </>
                    ) : null}
                    <Tooltip content="Reset transform" direction="top">
                      <IconButton
                        variant="plain"
                        title="Reset transform"
                        className="ask-h-7 ask-w-7"
                        onClick={() =>
                          handleTransform3DLayer(layer.id, {
                            resetTransform: true,
                          })
                        }
                      >
                        <AsukaIcon name="reset" size={12} />
                      </IconButton>
                    </Tooltip>
                  </div>
                ) : null}
              </div>
            );
          }}
        />
      </CardContent>
    </Card>
  );
};

registerModule({
  id: "layers",
  label: "Layers",
  icon: "layers",
  render: LayersModule,
  requiresView: true,
  supportedEngines: ["fabric", "3d"],
  requiredCapabilities: ["objects"],
});

export default LayersModule;
