
import { fabric } from '../fabric';
import { isRuntimeHelperRole } from './runtimeRoles';

export function createSelectionOutlineRuntime(params: {
  canvas: any;
  getDrawingZones: () => any[];
  createZoneClipObjectSync: (zone: any, rect: { left: number; top: number; width: number; height: number }) => any | null;
  normalizeSvgObjectToRect: (obj: any, rect: { left: number; top: number; width: number; height: number }) => void;
}) {
  const { canvas, getDrawingZones, createZoneClipObjectSync, normalizeSvgObjectToRect } = params;
  let selectedZoneOutlineRect: any = null;
  let selectedZoneOutlineId: string | null = null;
  let selectedZoneOutlineKey: string | null = null;
  const drawingZoneById = new Map<string, any>();

  const rebuildDrawingZoneIndex = () => {
    drawingZoneById.clear();
    for (const z of getDrawingZones() || []) {
      if (z?.id != null) drawingZoneById.set(String(z.id), z);
    }
  };

  const removeSelectedZoneOutline = () => {
    if (!selectedZoneOutlineRect) return;
    try { canvas.remove(selectedZoneOutlineRect); } catch {}
    selectedZoneOutlineRect = null;
    selectedZoneOutlineId = null;
    selectedZoneOutlineKey = null;
  };

  const applyOutlineStyle = (outline: any, shape: string) => {
    const common: any = {
      fill: 'transparent',
      stroke: '#60a5fa',
      strokeWidth: 0.75,
      strokeUniform: true,
      strokeDashArray: [4, 2],
      selectable: false,
      evented: false,
      hasControls: false,
      hasBorders: false,
      objectCaching: true,
      excludeFromExport: true,
      hoverCursor: 'default',
      absolutePositioned: true,
    };
    const applySvgOutlineDescendants = (node: any) => {
      if (!node) return;
      try {
        node.set?.({
          fill: 'transparent',
          stroke: '#60a5fa',
          strokeWidth: 0.75,
          strokeUniform: true,
          strokeDashArray: [4, 2],
          selectable: false,
          evented: false,
          hasControls: false,
          hasBorders: false,
          excludeFromExport: true,
          hoverCursor: 'default',
          objectCaching: true,
          absolutePositioned: true,
        });
      } catch {}
      try { (node as any).dirty = true; } catch {}
      try {
        const children = typeof node.getObjects === 'function' ? node.getObjects() : [];
        for (const child of children || []) applySvgOutlineDescendants(child);
      } catch {}
    };
    if (shape === 'rect') Object.assign(common, { rx: 8, ry: 8 });
    outline.set?.(common);
    if (shape === 'svg') applySvgOutlineDescendants(outline);
    try { (outline as any).dirty = true; } catch {}
    (outline as any)._asukaRole = 'drawingZoneOutline';
    (outline as any).itemGroup = 'Tools';
    return outline;
  };

  const createSelectedZoneOutlineObject = (z: any): any | null => {
    try {
      const shape = String((z as any)?.shape ?? 'rect');
      if (shape === 'svg' && typeof (z as any)?.sourceSvg === 'string' && (z as any).sourceSvg) return null;
      const outline = createZoneClipObjectSync(z as any, { left: z.left, top: z.top, width: z.width, height: z.height });
      if (!outline) return null;
      return applyOutlineStyle(outline, shape);
    } catch {
      return null;
    }
  };

  const ensureSvgSelectedZoneOutlineAsync = (z: any, zid: string, nextKey: string) => {
    const svgText = typeof (z as any)?.sourceSvg === 'string' ? String((z as any).sourceSvg) : '';
    if (!svgText) return;
    const load = (fabric as any).loadSVGFromString?.(svgText);
    const finalize = (res: any) => {
      try {
        const rawObjects = Array.isArray(res) ? (res[0] ?? []) : (res?.objects ?? []);
        const objects = Array.isArray(rawObjects) ? rawObjects.filter(Boolean) : (rawObjects ? [rawObjects] : []);
        const options = Array.isArray(res) ? (res[1] ?? {}) : (res?.options ?? {});
        let outline: any = null;
        const groupFn = (fabric as any).util?.groupSVGElements;
        if (objects.length === 1) outline = objects[0];
        if (!outline && typeof groupFn === 'function') {
          try { outline = groupFn(objects, options || {}); } catch {}
        }
        if (!outline && typeof (fabric as any).Group === 'function') {
          try { outline = new (fabric as any).Group(objects, options || {}); } catch {}
        }
        if (!outline) return;
        try { outline.set({ originX: 'left', originY: 'top', left: Number(z.left ?? 0), top: Number(z.top ?? 0), absolutePositioned: true }); } catch {}
        normalizeSvgObjectToRect(outline, { left: z.left, top: z.top, width: z.width, height: z.height });
        applyOutlineStyle(outline, 'svg');
        if (selectedZoneOutlineKey !== nextKey || selectedZoneOutlineId !== zid) return;
        removeSelectedZoneOutline();
        selectedZoneOutlineRect = outline;
        selectedZoneOutlineId = zid;
        selectedZoneOutlineKey = nextKey;
        canvas.add(selectedZoneOutlineRect);
        applyOutlineStyle(selectedZoneOutlineRect, 'svg');
        try { canvas.bringToFront?.(selectedZoneOutlineRect); } catch {}
        try { canvas.requestRenderAll?.(); } catch { try { canvas.renderAll?.(); } catch {} }
      } catch {}
    };
    try {
      if (load && typeof load.then === 'function') load.then((res: any) => finalize(res)).catch(() => {});
      else (fabric as any).loadSVGFromString(svgText, (objects: any[], options: any) => finalize({ objects, options }));
    } catch {}
  };

  const ensureSelectedZoneOutline = (z: any, zid: string) => {
    try {
      const shape = String((z as any)?.shape ?? 'rect');
      const nextKey = [zid, shape, Number((z as any)?.left ?? 0), Number((z as any)?.top ?? 0), Number((z as any)?.width ?? 0), Number((z as any)?.height ?? 0)].join('|');
      if (selectedZoneOutlineRect && selectedZoneOutlineId === zid && selectedZoneOutlineKey === nextKey) {
        // Canvas resize/view lifecycle hooks may rebind generic controls after
        // this helper was created. Reassert its non-interactive contract every
        // time the outline is refreshed, before it is brought above user layers.
        applyOutlineStyle(selectedZoneOutlineRect, shape);
        try { canvas.bringToFront?.(selectedZoneOutlineRect); } catch {}
        return;
      }
      removeSelectedZoneOutline();
      selectedZoneOutlineId = zid;
      selectedZoneOutlineKey = nextKey;
      selectedZoneOutlineRect = createSelectedZoneOutlineObject(z);
      if (selectedZoneOutlineRect) {
        canvas.add(selectedZoneOutlineRect);
        applyOutlineStyle(selectedZoneOutlineRect, shape);
        canvas.bringToFront?.(selectedZoneOutlineRect);
      } else if (shape === 'svg') {
        ensureSvgSelectedZoneOutlineAsync(z, zid, nextKey);
      }
    } catch {}
  };

  const syncSelectedZoneOutlineFromSelection = () => {
    try {
      const active = canvas.getActiveObject?.();
      if (!active) return removeSelectedZoneOutline();
      const role = (active as any)._asukaRole;
      if (isRuntimeHelperRole(role) || role === 'drawingZone') return removeSelectedZoneOutline();
      const zid = (active as any)?._asukaZoneId as string | undefined;
      if (!zid) return removeSelectedZoneOutline();
      const z = drawingZoneById.get(zid);
      if (!z) return removeSelectedZoneOutline();
      ensureSelectedZoneOutline(z, zid);
    } catch {
      removeSelectedZoneOutline();
    }
  };

  const refreshSelectedZoneOutline = () => {
    try {
      if (selectedZoneOutlineRect && selectedZoneOutlineId) {
        const zz = drawingZoneById.get(selectedZoneOutlineId);
        if (zz) ensureSelectedZoneOutline(zz, selectedZoneOutlineId);
        else removeSelectedZoneOutline();
      }
    } catch {}
  };

  const reset = () => {
    selectedZoneOutlineRect = null;
    selectedZoneOutlineId = null;
    selectedZoneOutlineKey = null;
  };

  rebuildDrawingZoneIndex();

  return {
    rebuildDrawingZoneIndex,
    removeSelectedZoneOutline,
    ensureSelectedZoneOutline,
    syncSelectedZoneOutlineFromSelection,
    refreshSelectedZoneOutline,
    reset,
    getState: () => ({ selectedZoneOutlineRect, selectedZoneOutlineId, selectedZoneOutlineKey }),
  };
}
