import Konva from "konva";
import type { EditorObject } from "../../../store/editorStore";
import { getObjectBounds } from "../../../store/editorStore.helpers";

export type AlignKind =
  | "left"
  | "centerH"
  | "right"
  | "top"
  | "centerV"
  | "bottom";

export type BoundsRect = {
  minX: number;
  minY: number;
  maxX: number;
  maxY: number;
};

/** مرز آمار واقعی روی لایه (فونت RTL، چرخش، mask، …)؛ هم‌نس با مختصات ذخیره‌شده در استور */
export function boundsFromKonvaLayer(
  objId: string,
  layer: Konva.Layer | null | undefined,
): BoundsRect | null {
  if (!layer) return null;
  const node =
    layer.findOne(`#${objId}`) ?? layer.findOne(`#masked-${objId}`);
  if (!node) return null;

  try {
    const r = node.getClientRect({
      relativeTo: layer,
      skipStroke: false,
      skipShadow: true,
    });
    return {
      minX: r.x,
      minY: r.y,
      maxX: r.x + r.width,
      maxY: r.y + r.height,
    };
  } catch {
    return null;
  }
}

export function unionKonvaBoundsOfGroupMembers(
  groupId: string,
  objects: EditorObject[],
  layer: Konva.Layer | null | undefined,
): BoundsRect | null {
  const members = objects.filter(
    (o) =>
      o.groupId === groupId &&
      o.type !== "group" &&
      o.visible !== false,
  );
  if (members.length === 0) return null;

  let minX = Infinity;
  let minY = Infinity;
  let maxX = -Infinity;
  let maxY = -Infinity;
  let any = false;

  for (const m of members) {
    const b = boundsFromKonvaLayer(m.id, layer) ?? getObjectBounds(m);
    any = true;
    minX = Math.min(minX, b.minX);
    minY = Math.min(minY, b.minY);
    maxX = Math.max(maxX, b.maxX);
    maxY = Math.max(maxY, b.maxY);
  }

  return any ? { minX, minY, maxX, maxY } : null;
}

export const pageBounds = (width: number, height: number): BoundsRect => ({
  minX: 0,
  minY: 0,
  maxX: width,
  maxY: height,
});

/** مرز واقعی اعضای یک گروه (نه مستطیل ذخیره‌شدهٔ شیء group که ممکن است قدیمی باشد) */
export function unionBoundsOfGroupMembers(
  groupId: string,
  objects: EditorObject[],
): BoundsRect | null {
  const members = objects.filter(
    (o) =>
      o.groupId === groupId &&
      o.type !== "group" &&
      o.visible !== false,
  );
  return unionBounds(members);
}

/**
 * مرزی که برای هم‌ترازی استفاده می‌شود؛ برای گروه از اتحاد اعضا تا با لبهٔ صفحه هم‌پوشانی دقیق باشد.
 */
export function boundsForAlign(
  obj: EditorObject,
  objects: EditorObject[],
  layer?: Konva.Layer | null,
): BoundsRect {
  if (layer) {
    if (obj.type === "group") {
      const k = unionKonvaBoundsOfGroupMembers(obj.id, objects, layer);
      if (k) return k;
    } else {
      const k = boundsFromKonvaLayer(obj.id, layer);
      if (k) return k;
    }
  }

  if (obj.type !== "group") {
    return getObjectBounds(obj);
  }
  const fromMembers = unionBoundsOfGroupMembers(obj.id, objects);
  return fromMembers ?? getObjectBounds(obj);
}

export function unionBounds(objects: EditorObject[]): BoundsRect | null {
  let minX = Infinity;
  let minY = Infinity;
  let maxX = -Infinity;
  let maxY = -Infinity;
  let any = false;

  for (const obj of objects) {
    if (obj.visible === false) continue;
    const b = getObjectBounds(obj);
    any = true;
    minX = Math.min(minX, b.minX);
    minY = Math.min(minY, b.minY);
    maxX = Math.max(maxX, b.maxX);
    maxY = Math.max(maxY, b.maxY);
  }

  return any ? { minX, minY, maxX, maxY } : null;
}

/** اتحاد مرز هم‌ترازیِ چند هدف (گروه‌ها با اعضایشان) */
export function unionBoundsOfAlignTargets(
  targets: EditorObject[],
  allObjects: EditorObject[],
  layer?: Konva.Layer | null,
): BoundsRect | null {
  let minX = Infinity;
  let minY = Infinity;
  let maxX = -Infinity;
  let maxY = -Infinity;
  let any = false;

  for (const obj of targets) {
    if (obj.visible === false) continue;
    const b = boundsForAlign(obj, allObjects, layer);
    any = true;
    minX = Math.min(minX, b.minX);
    minY = Math.min(minY, b.minY);
    maxX = Math.max(maxX, b.maxX);
    maxY = Math.max(maxY, b.maxY);
  }

  return any ? { minX, minY, maxX, maxY } : null;
}

export function deltasForAlign(
  obj: EditorObject,
  kind: AlignKind,
  target: BoundsRect,
  objects: EditorObject[],
  layer?: Konva.Layer | null,
): { dx: number; dy: number } {
  const b = boundsForAlign(obj, objects, layer);
  const midX = (b.minX + b.maxX) / 2;
  const midY = (b.minY + b.maxY) / 2;
  const tMidX = (target.minX + target.maxX) / 2;
  const tMidY = (target.minY + target.maxY) / 2;

  let dx = 0;
  let dy = 0;

  switch (kind) {
    case "left":
      dx = target.minX - b.minX;
      break;
    case "centerH":
      dx = tMidX - midX;
      break;
    case "right":
      dx = target.maxX - b.maxX;
      break;
    case "top":
      dy = target.minY - b.minY;
      break;
    case "centerV":
      dy = tMidY - midY;
      break;
    case "bottom":
      dy = target.maxY - b.maxY;
      break;
    default:
      break;
  }

  return {
    dx: kind === "left" || kind === "centerH" || kind === "right" ? Math.round(dx) : 0,
    dy: kind === "top" || kind === "centerV" || kind === "bottom" ? Math.round(dy) : 0,
  };
}

/** قاب گروه را مطابق اتحاد اعضا بدون جابجاییٔ اعضا تنظیم می‌کند */
export function computeGroupFrameUpdate(
  groupId: string,
  objects: EditorObject[],
  layer?: Konva.Layer | null,
): Partial<EditorObject> | null {
  const u =
    (layer && unionKonvaBoundsOfGroupMembers(groupId, objects, layer)) ||
    unionBoundsOfGroupMembers(groupId, objects);
  if (!u) return null;
  return {
    x: u.minX,
    y: u.minY,
    width: u.maxX - u.minX,
    height: u.maxY - u.minY,
  };
}

export function updatesFromDeltas(
  obj: EditorObject,
  dx: number,
  dy: number,
): Partial<EditorObject> {
  if (dx === 0 && dy === 0) return {};

  if (obj.type === "line" || obj.type === "arrow") {
    return {
      x: (obj.x ?? 0) + dx,
      y: (obj.y ?? 0) + dy,
      width: (obj.width ?? 0) + dx,
      height: (obj.height ?? 0) + dy,
    };
  }

  return {
    x: (obj.x ?? 0) + dx,
    y: (obj.y ?? 0) + dy,
  };
}
