import React, {
  forwardRef,
  useCallback,
  useEffect,
  useImperativeHandle,
  useMemo,
  useRef,
  useState,
} from 'react';

import Konva from 'konva';
import {
  ArmchairIcon,
  BrickWall,
  ClipboardCheck,
  CopyIcon,
  Edit2Icon,
  Grid2X2,
  PlusCircle,
  RowsIcon,
  Save,
  SquareIcon,
  TheaterIcon,
  TrashIcon,
  Undo2,
  VaultIcon,
  ZoomIn,
  ZoomOut,
} from 'lucide-react';
import { MdOutlineTableRestaurant } from 'react-icons/md';
import {
  Layer,
  Line,
  Rect,
  Stage,
  Text,
  Transformer,
} from 'react-konva';

import { HallElementType } from '@/api/types';
import { toast } from '@/componentsRoot/ToastRoot';
import { Button } from '@/components/ui/button';
import {
  Dialog,
  DialogContent,
  DialogFooter,
} from '@/components/ui/dialog';
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import {
  Tooltip,
  TooltipContent,
  TooltipTrigger,
} from '@/components/ui/tooltip';
import { Lang } from '@/lang/Lang';

import {
  Grid,
  GridCol,
  GridRow,
} from '@/components/ui/grid';
import SeatNumberingDialog from './components/SeatNumberingDialog';
import SeatRowsDialog from './components/SeatRowsDialog';
import SeatSettingsDialog from './components/SeatSettingsDialog';
import SectorSettingsDialog from './components/SectorSettingsDialog';
import StageSettingsDialog from './components/StageSettingsDialog';
import {
  buildHallElementsArray,
  coerceHallElementsSingleUUID,
  hallElementsFromInitValue,
  makeHallElementId,
  reconcileSeatSectorsLayout,
  sortHallElementsByPaintOrder,
} from './hallFlat';
import {
  HALL_SEAT_FILL_DIRECTION_DEFAULT_ROWS,
  HALL_SEAT_FILL_DIRECTION_DEFAULT_SEAT_NUMBERS,
  type HallSeatFillDirection,
  hallSeatsGroupedLinesOrdered,
} from './hallSeatOrder';
import {
  hallSeatLabelAt,
  parseHallSeatStart,
} from './hallSeatStartLabel';
import {
  buildHallSeatSmartLabels,
  getHallBlockCornerRadius,
  getHallBlockOnShapeTextFill,
  getHallSeatCornerRadius,
  HALL_CANVAS,
  HALL_CHECK_FLASH_MS,
  HALL_DUPLICATE_HIGHLIGHT_STROKE,
  HALL_EDITOR_GRID,
  HALL_ELEMENT_DEFAULTS,
  HALL_SEAT_DRAFT,
  HALL_SEAT_GAP,
  HALL_SEAT_SIZE,
  HALL_SECTOR,
  HALL_SELECTION,
  HALL_STAGE_SIZE,
  HALL_ZOOM,
  type HallElementPaletteDefaults,
  hallSeatLabelFill,
  hallSeatLabelFontSizeForRect,
  hallSeatRowLabelFill,
  hallSeatRowLabelFontSizeForRect,
  resolveHallElementFill,
} from './hallTheme';

type DialogHallEditorOpenParams = {
    /** Массив `HallElementType` или старый объект с полями `seats` / `elements`. */
    initValue?: unknown;
    onSave?: (value: HallElementType[]) => void;
}
type DialogHallEditorRef = {
    /** Открыть редактор; `initValue` — массив элементов или старый объект зала. */
    open: (params: DialogHallEditorOpenParams) => void;
    close: () => void;
}

const HALL_UNDO_MAX = 3;

const findInLayout = (els: any[], id: string): any | undefined => els.find((e) => e._id === id);

const mapItemInLayout = (els: any[], id: string, fn: (e: any) => any): any[] => (
    els.map((e) => (e._id === id ? fn(e) : e))
);

const filterIdsFromLayout = (els: any[], drop: Set<string>): any[] => (
    els.filter((e) => !drop.has(e._id))
);

const appendToLayoutByType = (els: any[], el: any): any[] => (
    sortHallElementsByPaintOrder([...els, el])
);

const appendManyToLayout = (els: any[], newOnes: any[]): any[] => (
    sortHallElementsByPaintOrder([...els, ...newOnes])
);

const mapAllItemsInLayout = (els: any[], fn: (e: any) => any): any[] => els.map(fn);

type HallEditorUndoSnapshot = {
    layout: any[];
};

export const dialogHallEditorRef = React.createRef<DialogHallEditorRef>();




/** Геометрия и визуальные поля элемента в JSON (без логики места / вместимости). */
export type HallJsonLayoutFields = {
    _id: string;
    x: number;
    y: number;
    width: number;
    height: number;
    rotation: number;
    label: string;
};


type SeatDraftRect = {
    startX: number;
    startY: number;
    currentX: number;
    currentY: number;
};

/** Рисование области: сетка мест или один блок (сцена / стол / зона). */
type RectPlacementDraft =
    | ({ mode: 'seats' } & SeatDraftRect)
    | ({ mode: 'block'; blockType: 'stage' | 'table' | 'zone' | 'wall' } & SeatDraftRect);

type SelectionRect = {
    startX: number;
    startY: number;
    currentX: number;
    currentY: number;
    appendToSelection: boolean;
}

type SeatDraftDefaults = {
    w: number;
    h: number;
    halfW: number;
    halfH: number;
    gapX: number;
    gapY: number;
};

type SeatDraftGridItem = {
    x: number;
    y: number;
    w: number;
    h: number;
    halfW: number;
    halfH: number;
};

function buildSeatDraftGrid(
    draft: SeatDraftRect,
    seatDefaults: SeatDraftDefaults,
): {
    x: number;
    y: number;
    width: number;
    height: number;
    cols: number;
    rows: number;
    total: number;
    items: SeatDraftGridItem[];
} {
    const width = Math.abs(draft.currentX - draft.startX);
    const height = Math.abs(draft.currentY - draft.startY);
    const pitchX = seatDefaults.w + seatDefaults.gapX;
    const pitchY = seatDefaults.h + seatDefaults.gapY;
    const cols = Math.max(1, Math.floor((width - seatDefaults.w + seatDefaults.gapX) / pitchX) + 1);
    const rows = Math.max(1, Math.floor((height - seatDefaults.h + seatDefaults.gapY) / pitchY) + 1);
    const x = Math.min(draft.startX, draft.currentX);
    const y = Math.min(draft.startY, draft.currentY);
    const items: SeatDraftGridItem[] = [];

    for (let row = 0; row < rows; row++) {
        for (let col = 0; col < cols; col++) {
            items.push({
                x: x + seatDefaults.halfW + col * pitchX,
                y: y + seatDefaults.halfH + row * pitchY,
                w: seatDefaults.w,
                h: seatDefaults.h,
                halfW: seatDefaults.halfW,
                halfH: seatDefaults.halfH,
            });
        }
    }

    return {
        x,
        y,
        width,
        height,
        cols,
        rows,
        total: cols * rows,
        items,
    };
}

/** Значения по умолчанию для мест: явные `null`, чтобы в JSON поля всегда присутствовали. */
const DEFAULT_SEAT_LOGIC: Pick<any, 'seatNumber' | 'seatRow' | 'sector'> = {
    seatNumber: undefined,
    seatRow: undefined,
    sector: undefined,
};

const PALETTE: Record<string, HallElementPaletteDefaults & { icon?: React.ReactNode }> = {
    seat: {
        icon: <ArmchairIcon />,
        ...HALL_ELEMENT_DEFAULTS.seat,
        ...DEFAULT_SEAT_LOGIC,
    },
    table: {
        icon: <MdOutlineTableRestaurant size={24} />,
        ...HALL_ELEMENT_DEFAULTS.table,
    },

    stage: {
        icon: <TheaterIcon />,
        ...HALL_ELEMENT_DEFAULTS.stage,
    },
    wall: {
        icon: <BrickWall />,
        ...HALL_ELEMENT_DEFAULTS.wall,
    },
    sector: {
        icon: <SquareIcon />,
        ...HALL_ELEMENT_DEFAULTS.sector,
    },
    zone: {
        icon: <Grid2X2 />,
        ...HALL_ELEMENT_DEFAULTS.zone,
    },
};

function seatSize(el: any): { w: number; h: number; halfW: number; halfH: number } {
    const wRaw = el?.width;
    const hRaw = el?.height;
    const wNum = typeof wRaw === 'number' ? wRaw : Number(wRaw);
    const hNum = typeof hRaw === 'number' ? hRaw : Number(hRaw);
    if (!(Number.isFinite(wNum) && wNum > 0) || !(Number.isFinite(hNum) && hNum > 0)) {
        console.warn('[DialogHallEditor] Invalid seat size, fallback to default size', {
            seatId: el?._id,
            width: wRaw,
            height: hRaw,
            fallback: HALL_SEAT_SIZE,
        });
    }
    const w = Number.isFinite(wNum) && wNum > 0 ? wNum : HALL_SEAT_SIZE;
    const h = Number.isFinite(hNum) && hNum > 0 ? hNum : HALL_SEAT_SIZE;
    return { w, h, halfW: w / 2, halfH: h / 2 };
}

function hallCenteredRectBounds(
    centerX: number,
    centerY: number,
    width: number,
    height: number,
    rotation = 0,
): { x: number; y: number; width: number; height: number } {
    const angle = ((rotation ?? 0) * Math.PI) / 180;
    const cos = Math.cos(angle);
    const sin = Math.sin(angle);
    const bboxWidth = Math.abs(width * cos) + Math.abs(height * sin);
    const bboxHeight = Math.abs(width * sin) + Math.abs(height * cos);
    return {
        x: centerX - bboxWidth / 2,
        y: centerY - bboxHeight / 2,
        width: bboxWidth,
        height: bboxHeight,
    };
}

/** Bounding box для hit-test и группового выделения (место — квадрат вокруг центра `x,y`). */
function hallElementBounds(el: any): { x: number; y: number; width: number; height: number } {
    if (el.type === 'seat') {
        const { w, h } = seatSize(el);
        return hallCenteredRectBounds(el.x, el.y, w, h, el.rotation ?? 0);
    }
    return { x: el.x, y: el.y, width: el.width, height: el.height };
}

function rectIntersectsRect(
    a: { x: number; y: number; width: number; height: number },
    b: { x: number; y: number; width: number; height: number },
): boolean {
    return !(
        a.x > b.x + b.width
        || a.x + a.width < b.x
        || a.y > b.y + b.height
        || a.y + a.height < b.y
    );
}

const normalizeSeatLogicRecord = (s: any) => ({
    seatNumber: (s.seatNumber == null || String(s.seatNumber).trim() === '') ? null : String(s.seatNumber).trim(),
    seatRow: (s.seatRow == null || String(s.seatRow).trim() === '') ? null : String(s.seatRow).trim(),
    sector: (s.sector == null || s.sector === '') ? null : String(s.sector).trim(),
});

const seatLogicSignature = (s: any) => JSON.stringify({
    type: s.type,
    ...normalizeSeatLogicRecord(s),
});

/** Дубликаты: места с одинаковой «логикой»; сектора/зоны с одинаковым названием. */
const collectHallDuplicateIdsFromLayout = (els: any[]): string[] => {
    const dup = new Set<string>();

    const bySeatSig = new Map<string, string[]>();
    els.filter((e) => e.type === 'seat').forEach((s) => {
        const sig = seatLogicSignature(s);
        const arr = bySeatSig.get(sig) ?? [];
        arr.push(s._id);
        bySeatSig.set(sig, arr);
    });
    bySeatSig.forEach((ids) => {
        if (ids.length > 1) {
            ids.forEach((id) => dup.add(id));
        }
    });

    const namedSectors = els.filter((e) => e.type === 'sector' || e.type === 'zone');
    const byLabel = new Map<string, string[]>();
    namedSectors.forEach((s) => {
        const key = (s.label ?? '').trim().toLowerCase();
        if (!key) {
            return;
        }
        const arr = byLabel.get(key) ?? [];
        arr.push(s._id);
        byLabel.set(key, arr);
    });
    byLabel.forEach((ids) => {
        if (ids.length > 1) {
            ids.forEach((id) => dup.add(id));
        }
    });

    return Array.from(dup);
};

const createElement = (type: string, x: number, y: number): any => {
    const base = PALETTE[type];
    return {
        _id: makeHallElementId(),
        type,
        x,
        y,
        width: base.width,
        height: base.height,
        rotation: 0,
        label: type === 'sector'
            ? 'Новый сектор'
            : (type === 'zone' ? 'Новый сектор без мест' : base.label),
        ...(type === 'seat' ? DEFAULT_SEAT_LOGIC : {}),
    };
};

const DialogHallEditor = forwardRef<DialogHallEditorRef, {}>((props, ref) => {
    const [open, setOpen] = useState(false); // Не трогать для теста всегда открыт
    const [layout, setLayout] = useState<any[]>([]);
    const [selectedIds, setSelectedIds] = useState<string[]>([]);
    const [toolForClickCreate, setToolForClickCreate] = useState<string | null>(null);
    const [zoom, setZoom] = useState(1);
    const [rectPlacementDraft, setRectPlacementDraft] = useState<RectPlacementDraft | null>(null);
    const [selectionRect, setSelectionRect] = useState<SelectionRect | null>(null);
    const [seatNumberingOpen, setSeatNumberingOpen] = useState(false);
    const [seatNumberStart, setSeatNumberStart] = useState('1');
    const [seatNumberFillDirection, setSeatNumberFillDirection] = useState<HallSeatFillDirection>(
        HALL_SEAT_FILL_DIRECTION_DEFAULT_SEAT_NUMBERS,
    );
    const [seatRowsOpen, setSeatRowsOpen] = useState(false);
    const [seatRowStart, setSeatRowStart] = useState('1');
    const [seatRowFillDirection, setSeatRowFillDirection] = useState<HallSeatFillDirection>(
        HALL_SEAT_FILL_DIRECTION_DEFAULT_ROWS,
    );
    const layoutRef = useRef(layout);
    layoutRef.current = layout;

    const hallUndoRef = useRef<HallEditorUndoSnapshot[]>([]);
    const [seatSettingsOpen, setSeatSettingsOpen] = useState(false);
    const [seatSettingsSeatId, setSeatSettingsSeatId] = useState<string | null>(null);
    const [seatSettingsSeatNumber, setSeatSettingsSeatNumber] = useState<string>('');
    const [seatSettingsSeatRow, setSeatSettingsSeatRow] = useState<string>('');
    const [sectorSettingsOpen, setSectorSettingsOpen] = useState(false);
    const [sectorSettingsId, setSectorSettingsId] = useState<string | null>(null);
    const [sectorSettingsName, setSectorSettingsName] = useState('');
    const [sectorSettingsIsNoSeats, setSectorSettingsIsNoSeats] = useState(false);
    const [freezeSectorSelection, setFreezeSectorSelection] = useState(false);
    const [stageSettingsOpen, setStageSettingsOpen] = useState(false);
    const [stageSettingsId, setStageSettingsId] = useState<string | null>(null);
    const [stageSettingsName, setStageSettingsName] = useState('');

    const [hallCheckFlash, setHallCheckFlash] = useState<{ ids: string[]; startedAt: number } | null>(null);
    const [hallCheckPulseMs, setHallCheckPulseMs] = useState(0);

    const stageRef = useRef<any>(null);
    const wrapperRef = useRef<HTMLDivElement | null>(null);
    const trRef = useRef<any>(null);
    const nodeRefs = useRef<Record<string, any>>({});
    const dragGroupStartRef = useRef<Record<string, { x: number; y: number }>>({});
    const dragGroupSelectedIdsRef = useRef<string[]>([]);
    const activeDragRef = useRef<{ id: string; startX: number; startY: number } | null>(null);
    const dragGroupPointerStartRef = useRef<{ x: number; y: number } | null>(null);
    const singleDragStartRef = useRef<{ id: string; x: number; y: number } | null>(null);
    const selectionFrameDragStartRef = useRef<Record<string, { x: number; y: number }>>({});
    const selectionFramePointerStartRef = useRef<{ x: number; y: number } | null>(null);

    const onSaveRef = useRef<((value: HallElementType[]) => void) | null>(null);

    useImperativeHandle(ref, () => ({
        open: (params: DialogHallEditorOpenParams) => {

            setOpen(true);

            onSaveRef.current = params.onSave ?? null;
            const raw = hallElementsFromInitValue(params.initValue ?? null);
            const hallUuidCoerce = coerceHallElementsSingleUUID(raw);
            if (!hallUuidCoerce.ok) {
                toast.error(Lang.key('hall_uuid_unique_error'));
            }
            setLayout(hallUuidCoerce.ok ? hallUuidCoerce.elements : raw);
            setSelectedIds([]);
            hallUndoRef.current = [];
        },
        close: () => {
            setOpen(false);
        }
    }));

    const handleSave = () => {



        if (!runHallCheck()) {
            return;
        }


        onSaveRef.current?.(buildHallElementsArray(layout));
        setOpen(false);
    }

    // NOTE: Transformer нужно кормить нодами, которые уже попали в refs.
    // Поэтому вычисляем их в эффекте, а не во время рендера.
    const seatDefaults = useMemo(() => {
        const seats = layout.filter((e) => e.type === 'seat');
        const ws = seats.map((s) => seatSize(s).w);
        const hs = seats.map((s) => seatSize(s).h);
        const average = (values: number[], fallback: number) => {
            if (!values.length) {
                console.warn('[DialogHallEditor] Failed to calculate average seat size, using default size', {
                    reason: 'no-seat-sizes',
                    fallback,
                });
                return fallback;
            }
            const sum = values.reduce((acc, value) => acc + value, 0);
            const v = sum / values.length;
            if (!(Number.isFinite(v) && v > 0)) {
                console.warn('[DialogHallEditor] Failed to calculate average seat size, using default size', {
                    reason: 'invalid-average',
                    values,
                    fallback,
                });
            }
            return Number.isFinite(v) && v > 0 ? v : fallback;
        };
        const w = average(ws, HALL_SEAT_SIZE);
        const h = average(hs, HALL_SEAT_SIZE);
        return {
            w,
            h,
            halfW: w / 2,
            halfH: h / 2,
            tol: Math.min(w, h) / 2,
            gapX: HALL_SEAT_GAP,
            gapY: HALL_SEAT_GAP,
        };
    }, [layout]);

    const seatDraftStats = useMemo(() => {
        if (!rectPlacementDraft || rectPlacementDraft.mode !== 'seats') {
            return null;
        }
        return buildSeatDraftGrid(rectPlacementDraft, seatDefaults);
    }, [rectPlacementDraft, seatDefaults]);
    const blockPlacementPreview = useMemo(() => {
        if (!rectPlacementDraft || rectPlacementDraft.mode !== 'block') {
            return null;
        }
        const d = rectPlacementDraft;
        const left = Math.min(d.startX, d.currentX);
        const top = Math.min(d.startY, d.currentY);
        let width = Math.abs(d.currentX - d.startX);
        let height = Math.abs(d.currentY - d.startY);
        const base = PALETTE[d.blockType];
        if (width < 10) {
            width = base.width;
        }
        if (height < 10) {
            height = base.height;
        }
        return { x: left, y: top, width, height, blockType: d.blockType };
    }, [rectPlacementDraft]);
    const seatDraftPreviewSeats = useMemo(() => {
        if (!seatDraftStats) {
            return [];
        }
        return seatDraftStats.items;
    }, [seatDraftStats]);
    const normalizedSelectionRect = useMemo(() => {
        if (!selectionRect) {
            return null;
        }
        return {
            x: Math.min(selectionRect.startX, selectionRect.currentX),
            y: Math.min(selectionRect.startY, selectionRect.currentY),
            width: Math.abs(selectionRect.currentX - selectionRect.startX),
            height: Math.abs(selectionRect.currentY - selectionRect.startY),
        };
    }, [selectionRect]);
    const selectedSeats = useMemo(
        () => layout.filter((e) => e.type === 'seat' && selectedIds.includes(e._id)),
        [layout, selectedIds]
    );
    const selectedSingleElement = useMemo(() => {
        if (selectedIds.length !== 1) {
            return null;
        }
        return findInLayout(layout, selectedIds[0]!) ?? null;
    }, [layout, selectedIds]);
    const showHallEditStage = selectedSingleElement?.type === 'stage';
    const showHallEditSector = selectedSingleElement?.type === 'sector' || selectedSingleElement?.type === 'zone';
    const showHallSeatActions = selectedSeats.length > 0;
    const selectedElements = useMemo(
        () => selectedIds.map((id) => findInLayout(layout, id)).filter((x): x is any => x != null),
        [layout, selectedIds]
    );
    const layoutSectorsAndZones = useMemo(
        () => layout.filter((e) => e.type === 'sector' || e.type === 'zone'),
        [layout],
    );
    const layoutBlocks = useMemo(
        () => layout.filter((e) => (
            e.type === 'stage' || e.type === 'table' || e.type === 'wall' || e.type === 'seat'
        )),
        [layout],
    );
    const seatSmartLabels = useMemo(
        () => buildHallSeatSmartLabels(layout.filter((e) => e.type === 'seat')),
        [layout],
    );
    const selectionBounds = useMemo(() => {
        if (selectedElements.length < 2) {
            return null;
        }
        let minX = Number.POSITIVE_INFINITY;
        let minY = Number.POSITIVE_INFINITY;
        let maxX = Number.NEGATIVE_INFINITY;
        let maxY = Number.NEGATIVE_INFINITY;
        selectedElements.forEach((el) => {
            const b = hallElementBounds(el);
            minX = Math.min(minX, b.x);
            minY = Math.min(minY, b.y);
            maxX = Math.max(maxX, b.x + b.width);
            maxY = Math.max(maxY, b.y + b.height);
        });
        if (!Number.isFinite(minX) || !Number.isFinite(minY) || !Number.isFinite(maxX) || !Number.isFinite(maxY)) {
            return null;
        }
        return {
            x: minX,
            y: minY,
            width: Math.max(1, maxX - minX),
            height: Math.max(1, maxY - minY),
        };
    }, [selectedElements]);

    const hallEditorGridLines = useMemo(() => {
        const { step, stroke, strokeWidth } = HALL_EDITOR_GRID;
        const w = HALL_STAGE_SIZE;
        const nodes: React.ReactNode[] = [];
        let k = 0;
        for (let x = 0; x <= w; x += step) {
            nodes.push(
                <Line
                    key={`hall-grid-v-${k++}`}
                    points={[x, 0, x, w]}
                    stroke={stroke}
                    strokeWidth={strokeWidth}
                    listening={false}
                    perfectDrawEnabled={false}
                />
            );
        }
        for (let y = 0; y <= w; y += step) {
            nodes.push(
                <Line
                    key={`hall-grid-h-${k++}`}
                    points={[0, y, w, y]}
                    stroke={stroke}
                    strokeWidth={strokeWidth}
                    listening={false}
                    perfectDrawEnabled={false}
                />
            );
        }
        return nodes;
    }, []);

    useEffect(() => {
        if (!trRef.current) {
            return;
        }

        const rafId = window.requestAnimationFrame(() => {
            const nodes = selectedIds
                .map((id) => nodeRefs.current[id])
                .filter(Boolean);

            trRef.current.nodes(nodes);
            trRef.current.getLayer()?.batchDraw();
        });

        return () => window.cancelAnimationFrame(rafId);
    }, [selectedIds, layout]);

    useEffect(() => {
        if (!hallCheckFlash) {
            return;
        }
        let raf = 0;
        const start = hallCheckFlash.startedAt;
        const tick = () => {
            const elapsed = Date.now() - start;
            if (elapsed >= HALL_CHECK_FLASH_MS) {
                setHallCheckFlash(null);
                setHallCheckPulseMs(0);
                return;
            }
            setHallCheckPulseMs(elapsed);
            raf = requestAnimationFrame(tick);
        };
        raf = requestAnimationFrame(tick);
        return () => cancelAnimationFrame(raf);
    }, [hallCheckFlash]);

    const clampZoom = (value: number) => Math.min(HALL_ZOOM.max, Math.max(HALL_ZOOM.min, value));

    const rectsIntersect = (
        a: { x: number; y: number; width: number; height: number },
        b: { x: number; y: number; width: number; height: number }
    ) => !(
        a.x > b.x + b.width
        || a.x + a.width < b.x
        || a.y > b.y + b.height
        || a.y + a.height < b.y
    );

    const violatesNoSeatsSectorRuleLayout = (next: any[]) => {
        const seats = next.filter((e) => e.type === 'seat');
        const noSeatSectors = next.filter((e) => e.type === 'zone');
        if (!seats.length || !noSeatSectors.length) {
            return false;
        }
        for (const s of seats) {
            const { w, h } = seatSize(s);
            const seatRect = hallCenteredRectBounds(s.x, s.y, w, h, s.rotation ?? 0);
            for (const z of noSeatSectors) {
                const zoneRect = { x: z.x, y: z.y, width: z.width, height: z.height };
                if (rectIntersectsRect(seatRect, zoneRect)) {
                    return true;
                }
            }
        }
        return false;
    };

    /** Проверка по плоскому списку координат (после перетаскивания Konva). */
    const violatesNoSeatsSectorRule = (nextFlat: any[]) => (
        violatesNoSeatsSectorRuleLayout(nextFlat)
    );

    const runHallCheck = (): boolean => {
        const uuidRes = coerceHallElementsSingleUUID(layout);
        if (!uuidRes.ok) {
            toast.error(Lang.key('hall_uuid_unique_error'));
            return false;
        }
        const dupIds = collectHallDuplicateIdsFromLayout(layout);
        const ids = Array.from(new Set([...dupIds]));
        if (!ids.length) {
            setHallCheckFlash(null);
            setHallCheckPulseMs(0);
            toast.success(Lang.key('hall_without_errors'));
            return true;
        }
        setHallCheckFlash({ ids, startedAt: Date.now() });
        const parts: string[] = [];
        if (dupIds.length) {
            parts.push(Lang.key('duplicates_of_seats_or_sectors'));
        }
        toast.error(parts.length ? `${Lang.key('hall_with_errors')}: ${parts.join(', ')}` : Lang.key('hall_with_errors_settings'));
        return false;
    };

    const hallCheckOverlayOpacity = (t: number) => {
        const fadeIn = Math.min(1, t / 180);
        const fadeOut = t > HALL_CHECK_FLASH_MS - 520 ? (HALL_CHECK_FLASH_MS - t) / 520 : 1;
        const pulse = 0.42 + 0.58 * (0.5 + 0.5 * Math.sin(t * 0.014));
        return Math.max(0, Math.min(1, fadeIn * fadeOut * pulse));
    };

    const pushHallUndoSnapshot = useCallback(() => {
        hallUndoRef.current.push({
            layout: structuredClone(layoutRef.current),
        });
        while (hallUndoRef.current.length > HALL_UNDO_MAX) {
            hallUndoRef.current.shift();
        }
    }, []);

    const commitPreparedHallLayout = useCallback((next: any[]) => {
        const r = coerceHallElementsSingleUUID(next);
        if (!r.ok) {
            toast.error(Lang.key('hall_uuid_unique_error'));
            return false;
        }
        pushHallUndoSnapshot();
        setLayout(r.elements);
        return true;
    }, [pushHallUndoSnapshot]);

    const patchLayout = useCallback((
        fn: (els: any[]) => any[],
        reconcileAfter = true,
    ) => {
        const prev = layoutRef.current;
        let next = fn(prev);
        if (reconcileAfter) {
            next = reconcileSeatSectorsLayout(next);
        }
        next = sortHallElementsByPaintOrder(next);
        return commitPreparedHallLayout(next);
    }, [commitPreparedHallLayout]);

    const commitHallLayoutFromProposed = useCallback((proposed: any[]) => {
        const next = sortHallElementsByPaintOrder(reconcileSeatSectorsLayout(proposed));
        return commitPreparedHallLayout(next);
    }, [commitPreparedHallLayout]);

    const undoLastHallChange = useCallback(() => {
        const stack = hallUndoRef.current;
        if (!stack.length) {
            toast.error(Lang.key('hall_undo_impossible'));
            return;
        }
        const snap = stack[stack.length - 1]!;
        hallUndoRef.current = stack.slice(0, -1);
        setLayout(structuredClone(snap.layout));
        setSelectedIds((ids) => ids.filter((id) => snap.layout.some((e) => e._id === id)));
    }, []);

    const duplicateSelected = useCallback(() => {
        if (!selectedIds.length) {
            return;
        }
        const sources = selectedIds
            .map((id) => findInLayout(layout, id))
            .filter((x): x is any => x != null);
        if (!sources.length) {
            return;
        }
        const copies: any[] = sources.map((source) => ({
            ...source,
            _id: makeHallElementId(),
            x: source.x + 24,
            y: source.y + 24,
        }));
        patchLayout((b) => appendManyToLayout(b, copies), true);
        setSelectedIds(copies.map((copy) => copy._id));
    }, [layout, selectedIds, patchLayout]);

    const deleteSelected = useCallback(() => {
        if (!selectedIds.length) {
            return;
        }
        const ids = new Set(selectedIds);
        patchLayout((b) => filterIdsFromLayout(b, ids), true);
        setSelectedIds([]);
    }, [selectedIds, patchLayout]);

    useEffect(() => {
        const onKeyDown = (e: KeyboardEvent) => {
            const target = e.target as HTMLElement | null;
            const isTypingTarget = Boolean(
                target
                && (
                    target.tagName === 'INPUT'
                    || target.tagName === 'TEXTAREA'
                    || target.isContentEditable
                )
            );
            if (isTypingTarget) {
                return;
            }
            if (!open || !selectedIds.length) {
                return;
            }
            if (e.key === 'Delete' || e.key === 'Backspace') {
                deleteSelected();
            }
            if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'd') {
                e.preventDefault();
                duplicateSelected();
            }
        };
        window.addEventListener('keydown', onKeyDown);
        return () => window.removeEventListener('keydown', onKeyDown);
    }, [open, selectedIds, deleteSelected, duplicateSelected]);

    const getPointerPositionInStage = () => {
        const stage = stageRef.current;
        if (!stage) {
            return { x: 40, y: 40 };
        }
        const pos = stage.getPointerPosition();
        if (!pos) {
            return { x: 40, y: 40 };
        }
        const transform = stage.getAbsoluteTransform().copy();
        transform.invert();
        return transform.point(pos);
    };

    const addElement = (type: string, x: number, y: number) => {
        const newElement = createElement(type, x, y);
        patchLayout((b) => appendToLayoutByType(b, newElement), true);
        setSelectedIds([newElement._id]);
    };

    const createSeatsInRect = (draft: Extract<RectPlacementDraft, { mode: 'seats' }>) => {
        const seatGrid = buildSeatDraftGrid(draft, seatDefaults);
        const seats: any[] = [];
        seatGrid.items.forEach((previewSeat) => {
            const seat = createElement('seat', previewSeat.x, previewSeat.y);
            seat.width = previewSeat.w;
            seat.height = previewSeat.h;
            seats.push(seat);
        });

        if (!seats.length) {
            return;
        }
        console.info('[DialogHallEditor] Creating seats from preview grid', {
            seatDefaults,
            grid: {
                cols: seatGrid.cols,
                rows: seatGrid.rows,
                total: seatGrid.total,
                width: seatGrid.width,
                height: seatGrid.height,
            },
            createdSeatSize: seats[0] ? { width: seats[0].width, height: seats[0].height } : null,
        });
        patchLayout((b) => appendManyToLayout(b, seats), true);
        setSelectedIds(seats.map((seat) => seat._id));
    };

    const createBlockInRect = (draft: Extract<RectPlacementDraft, { mode: 'block' }>) => {
        const left = Math.min(draft.startX, draft.currentX);
        const top = Math.min(draft.startY, draft.currentY);
        let width = Math.abs(draft.currentX - draft.startX);
        let height = Math.abs(draft.currentY - draft.startY);
        const base = PALETTE[draft.blockType];
        if (width < 10) {
            width = base.width;
        }
        if (height < 10) {
            height = base.height;
        }
        const newElement = createElement(draft.blockType, left, top);
        newElement.width = width;
        newElement.height = height;
        patchLayout((b) => appendToLayoutByType(b, newElement), true);
        setSelectedIds([newElement._id]);
    };

    const updateElement = (id: string, updater: (oldItem: any) => any) => {
        patchLayout((b) => mapItemInLayout(b, id, updater), true);
    };
    const applySeatNumbering = () => {
        if (!selectedSeats.length) {
            setSeatNumberingOpen(false);
            return;
        }
        const parsed = parseHallSeatStart(seatNumberStart);
        if (!parsed) {
            toast.error(Lang.key('hall_seat_start_invalid'));
            return;
        }

        const tol = seatDefaults.tol;
        const lines = hallSeatsGroupedLinesOrdered(selectedSeats, seatNumberFillDirection, tol);
        const seatNumbers = new Map<string, string>();
        lines.forEach((line, lineIdx) => {
            const label = hallSeatLabelAt(parsed, lineIdx);
            line.forEach((seat) => {
                seatNumbers.set(seat._id, label);
            });
        });

        patchLayout((b) => mapAllItemsInLayout(b, (item) => (
            seatNumbers.has(item._id)
                ? { ...item, seatNumber: seatNumbers.get(item._id)! }
                : item
        )), true);
        setSeatNumberingOpen(false);
    };
    const applySeatRows = () => {
        if (!selectedSeats.length) {
            setSeatRowsOpen(false);
            return;
        }
        const parsed = parseHallSeatStart(seatRowStart);
        if (!parsed) {
            toast.error(Lang.key('hall_seat_start_invalid'));
            return;
        }

        const tol = seatDefaults.tol;
        const lines = hallSeatsGroupedLinesOrdered(selectedSeats, seatRowFillDirection, tol);
        const seatRowsMap = new Map<string, string>();
        lines.forEach((line, lineIdx) => {
            const label = hallSeatLabelAt(parsed, lineIdx);
            line.forEach((seat) => {
                seatRowsMap.set(seat._id, label);
            });
        });

        patchLayout((b) => mapAllItemsInLayout(b, (item) => (
            seatRowsMap.has(item._id)
                ? { ...item, seatRow: seatRowsMap.get(item._id)! }
                : item
        )), true);
        setSeatRowsOpen(false);
    };

    const openSeatSettings = (seatId: string) => {
        const seat = layout.find((s) => s.type === 'seat' && s._id === seatId);
        if (!seat) {
            return;
        }
        setSelectedIds([seatId]);
        setSeatSettingsSeatId(seatId);
        setSeatSettingsSeatNumber(seat.seatNumber ?? '');
        setSeatSettingsSeatRow(seat.seatRow ?? '');
        setSeatSettingsOpen(true);
    };

    const saveSeatSettings = () => {
        if (!seatSettingsSeatId) {
            setSeatSettingsOpen(false);
            return;
        }
        const nextSeatNumber = seatSettingsSeatNumber.trim() ? seatSettingsSeatNumber.trim() : undefined;
        const nextSeatRow = seatSettingsSeatRow.trim() ? seatSettingsSeatRow.trim() : undefined;

        patchLayout((b) => mapAllItemsInLayout(b, (item) => {
            if (item._id !== seatSettingsSeatId || item.type !== 'seat') {
                return item;
            }
            return {
                ...item,
                seatNumber: nextSeatNumber,
                seatRow: nextSeatRow,
            };
        }), true);
        setSeatSettingsOpen(false);
    };

    const openSectorSettings = (sectorId: string) => {
        const sector = layout.find((s) => (s.type === 'sector' || s.type === 'zone') && s._id === sectorId);
        if (!sector) {
            return;
        }
        setSelectedIds([sectorId]);
        setSectorSettingsId(sectorId);
        const isNoSeats = sector.type === 'zone';
        setSectorSettingsIsNoSeats(isNoSeats);
        setSectorSettingsName(sector.label || (isNoSeats ? 'Новый сектор без мест' : 'Новый сектор'));
        setSectorSettingsOpen(true);
    };

    const saveSectorSettings = () => {
        if (!sectorSettingsId) {
            setSectorSettingsOpen(false);
            return;
        }
        const name = sectorSettingsName.trim() || (sectorSettingsIsNoSeats ? 'Новый сектор без мест' : 'Новый сектор');
        patchLayout((b) => mapAllItemsInLayout(b, (item) => {
            if (item._id !== sectorSettingsId) {
                return item;
            }
            if (item.type === 'sector' || item.type === 'zone') {
                return { ...item, label: name };
            }
            return item;
        }), true);
        setSectorSettingsOpen(false);
    };

    const isStage = (t: string) => t === 'stage';

    const openStageSettings = (stageId: string) => {
        const stage = layout.find((s) => s.type === 'stage' && s._id === stageId);
        if (!stage) {
            return;
        }
        setSelectedIds([stageId]);
        setStageSettingsId(stageId);
        setStageSettingsName(stage.label || 'Сцена');
        setStageSettingsOpen(true);
    };

    const saveStageSettings = () => {
        if (!stageSettingsId) {
            setStageSettingsOpen(false);
            return;
        }
        const name = stageSettingsName.trim() || 'Сцена';
        patchLayout((b) => mapAllItemsInLayout(b, (item) => (
            item._id === stageSettingsId && isStage(item.type as string)
                ? { ...item, label: name }
                : item
        )), false);
        setStageSettingsOpen(false);
    };

    return <Dialog
        open={open}
        onOpenChange={setOpen}
    >
        <DialogContent className="!w-[100vw] !max-w-[100vw] sm:!max-w-[100vw] h-[100vh] overflow-hidden !p-0" >

            {open && <Grid className='h-full w-full !w-[100vw] !max-w-[100vw] sm:!max-w-[100vw] h-[100vh]'>

                <GridCol className='bg-white'>

                    <GridRow autoSize className="p-2 gap-2 flex  flex-wrap items-center justify-between">

                        <div className='flex items-center gap-2'>
                            <Tooltip>
                                <TooltipTrigger asChild>
                                    <Button
                                        size="sm"
                                        type="button"
                                        variant="outline"
                                        aria-label={Lang.key('zoom_out')}
                                        onClick={() => setZoom((z) => clampZoom(Number((z - HALL_ZOOM.step).toFixed(2))))}
                                    >
                                        <ZoomOut />
                                    </Button>
                                </TooltipTrigger>
                                <TooltipContent>
                                    <p>{Lang.key('zoom_out')}</p>
                                </TooltipContent>
                            </Tooltip>

                            <Tooltip>
                                <TooltipTrigger asChild>
                                    <Button
                                        size="sm"
                                        type="button"
                                        variant="outline"
                                        aria-label={Lang.key('zoom_in')}
                                        onClick={() => setZoom((z) => clampZoom(Number((z + HALL_ZOOM.step).toFixed(2))))}
                                    >
                                        <ZoomIn />
                                    </Button>
                                </TooltipTrigger>
                                <TooltipContent>
                                    <p>{Lang.key('zoom_in')}</p>
                                </TooltipContent>
                            </Tooltip>

                            <DropdownMenu>
                                <DropdownMenuTrigger >
                                    <Tooltip>
                                        <TooltipTrigger >
                                            <Button
                                                size="sm"
                                                type="button"
                                                variant="outline"
                                                aria-label={Lang.key('create_hall_element')}
                                            >
                                                <PlusCircle />
                                            </Button>
                                        </TooltipTrigger>
                                        <TooltipContent>
                                            <p>{Lang.key('create_hall_element')}</p>
                                        </TooltipContent>
                                    </Tooltip>
                                </DropdownMenuTrigger>
                                <DropdownMenuContent className="w-[300px]" align="start">
                                    <DropdownMenuGroup>
                                        {(Object.keys(PALETTE) as string[]).map((type) => (
                                            <DropdownMenuItem
                                                key={type}
                                                style={{
                                                    backgroundColor: toolForClickCreate == type ? 'rgba(0, 0, 0, 0.05)' : 'transparent',
                                                }}
                                                onClick={() => {
                                                    if (toolForClickCreate == type) {
                                                        setToolForClickCreate(null);
                                                    } else {
                                                        setToolForClickCreate(type);
                                                    }
                                                }}
                                            >
                                                {PALETTE[type].icon}
                                                {' '}
                                                {PALETTE[type].label}
                                            </DropdownMenuItem>
                                        ))}
                                    </DropdownMenuGroup>
                                </DropdownMenuContent>
                            </DropdownMenu>



                            <Tooltip>
                                <TooltipTrigger asChild>
                                    <Button
                                        size="sm"
                                        type="button"
                                        variant={!freezeSectorSelection ? 'outline' : 'default'}
                                        aria-label={Lang.key('fix_hall_sector')}
                                        onClick={() => setFreezeSectorSelection((v) => !v)}
                                    >
                                        <VaultIcon />
                                    </Button>
                                </TooltipTrigger>
                                <TooltipContent>
                                    <p>{Lang.key('fix_hall_sector')}</p>
                                </TooltipContent>
                            </Tooltip>
                            <Tooltip>
                                <TooltipTrigger asChild>
                                    <Button
                                        size="sm"
                                        type="button"
                                        variant="outline"
                                        aria-label={Lang.key('hall_undo')}
                                        onClick={() => undoLastHallChange()}
                                    >
                                        <Undo2 />
                                    </Button>
                                </TooltipTrigger>
                                <TooltipContent>
                                    <p>{Lang.key('hall_undo')}</p>
                                </TooltipContent>
                            </Tooltip>




                        </div>



                        <div className='flex items-center gap-2'>






                            {showHallEditStage && (
                                <Tooltip>
                                    <TooltipTrigger asChild>
                                        <Button
                                            size="sm"
                                            type="button"
                                            variant="outline"
                                            aria-label={Lang.key('hall_edit_selected_stage')}
                                            onClick={() => openStageSettings(selectedIds[0])}
                                        >
                                            <Edit2Icon />
                                        </Button>
                                    </TooltipTrigger>
                                    <TooltipContent>
                                        <p>{Lang.key('hall_edit_selected_stage')}</p>
                                    </TooltipContent>
                                </Tooltip>
                            )}

                            {showHallEditSector && (
                                <Tooltip>
                                    <TooltipTrigger asChild>
                                        <Button
                                            size="sm"
                                            type="button"
                                            variant="outline"
                                            aria-label={Lang.key('hall_edit_selected_sector')}
                                            onClick={() => openSectorSettings(selectedIds[0])}
                                        >
                                            <Edit2Icon />
                                        </Button>
                                    </TooltipTrigger>
                                    <TooltipContent>
                                        <p>{Lang.key('hall_edit_selected_sector')}</p>
                                    </TooltipContent>
                                </Tooltip>
                            )}

                            {showHallSeatActions && (
                                <>
                                    <Tooltip>
                                        <TooltipTrigger asChild>
                                            <Button
                                                size="sm"
                                                type="button"
                                                variant="outline"
                                                aria-label={Lang.key('hall_set_seat_rows')}
                                                onClick={() => {
                                                    setSeatRowFillDirection(HALL_SEAT_FILL_DIRECTION_DEFAULT_ROWS);
                                                    setSeatRowsOpen(true);
                                                }}
                                            >
                                                <RowsIcon />
                                            </Button>
                                        </TooltipTrigger>
                                        <TooltipContent>
                                            <p>{Lang.key('hall_set_seat_rows')}</p>
                                        </TooltipContent>
                                    </Tooltip>
                                    <Tooltip>
                                        <TooltipTrigger asChild>
                                            <Button
                                                size="sm"
                                                type="button"
                                                variant="outline"
                                                aria-label={Lang.key('hall_set_seat_numbers')}
                                                onClick={() => {
                                                    setSeatNumberFillDirection(HALL_SEAT_FILL_DIRECTION_DEFAULT_SEAT_NUMBERS);
                                                    setSeatNumberingOpen(true);
                                                }}
                                            >
                                                <Grid2X2 />
                                            </Button>
                                        </TooltipTrigger>
                                        <TooltipContent>
                                            <p>{Lang.key('hall_set_seat_numbers')}</p>
                                        </TooltipContent>
                                    </Tooltip>
                                    <Tooltip>
                                        <TooltipTrigger asChild>
                                            <Button
                                                size="sm"
                                                type="button"
                                                variant="outline"
                                                aria-label={Lang.key('hall_copy_selected_seats')}
                                                onClick={() => duplicateSelected()}
                                            >
                                                <CopyIcon />
                                            </Button>
                                        </TooltipTrigger>
                                        <TooltipContent>
                                            <p>{Lang.key('hall_copy_selected_seats')}</p>
                                        </TooltipContent>
                                    </Tooltip>
                                </>
                            )}

                            {selectedIds.length > 0 && (
                                <Tooltip>
                                    <TooltipTrigger asChild>
                                        <Button
                                            size="sm"
                                            type="button"
                                            variant="outline"
                                            aria-label={Lang.key('hall_delete_selected')}
                                            onClick={() => deleteSelected()}
                                        >
                                            <TrashIcon />
                                        </Button>
                                    </TooltipTrigger>
                                    <TooltipContent>
                                        <p>{Lang.key('hall_delete_selected')}</p>
                                    </TooltipContent>
                                </Tooltip>
                            )}





                        </div>


                        <div
                            className='flex items-center gap-2'

                        >
                            <Tooltip>
                                <TooltipTrigger asChild>
                                    <Button
                                        size="sm"
                                        type="button"
                                        variant="outline"
                                        aria-label={Lang.key('hall_check')}
                                        onClick={() => {
                                            runHallCheck();
                                        }}
                                    >
                                        <ClipboardCheck />
                                    </Button>
                                </TooltipTrigger>
                                <TooltipContent>
                                    <p>{Lang.key('hall_check')}</p>
                                </TooltipContent>
                            </Tooltip>

                            <Tooltip>
                                <TooltipTrigger asChild>
                                    <Button
                                        onClick={() => {

                                            handleSave();
                                        }}
                                        size="sm"
                                        type="button"
                                        variant="outline"
                                        aria-label={Lang.key('hall_save_and_close')}
                                    >
                                        <Save />
                                    </Button>
                                </TooltipTrigger>
                                <TooltipContent>
                                    <p>{Lang.key('hall_save_and_close')}</p>
                                </TooltipContent>
                            </Tooltip>
                        </div>

                        <div></div>


                    </GridRow>
                    <GridRow>

                        <div
                            ref={wrapperRef}
                            className="flex-1 border rounded-md overflow-auto bg-white flex"
                            onDragOver={(e) => e.preventDefault()}
                            onDrop={(e) => {
                                e.preventDefault();
                                const type = e.dataTransfer.getData('hall-element-type') as string;
                                if (!type || !(type in PALETTE)) {
                                    return;
                                }
                                const stage = stageRef.current;
                                if (!stage) {
                                    return;
                                }
                                stage.setPointersPositions(e as any);
                                const pos = getPointerPositionInStage();
                                addElement(type, pos.x, pos.y);
                            }}
                        >
                            <div
                                style={{
                                    width: HALL_STAGE_SIZE * zoom,
                                    height: HALL_STAGE_SIZE * zoom,
                                    background: HALL_CANVAS.background,
                                    outline: HALL_CANVAS.outline,
                                    outlineOffset: '0px',
                                    margin: 'auto',
                                }}
                            >
                                <Stage
                                    ref={stageRef}
                                    width={HALL_STAGE_SIZE * zoom}
                                    height={HALL_STAGE_SIZE * zoom}
                                    scaleX={zoom}
                                    scaleY={zoom}
                                    onMouseDown={(e) => {
                                        const clickedOnEmpty = e.target === e.target.getStage();
                                        if (clickedOnEmpty) {
                                            if (toolForClickCreate) {
                                                const pos = getPointerPositionInStage();
                                                if (toolForClickCreate === 'seat') {
                                                    setRectPlacementDraft({
                                                        mode: 'seats',
                                                        startX: pos.x,
                                                        startY: pos.y,
                                                        currentX: pos.x,
                                                        currentY: pos.y,
                                                    });
                                                } else if (
                                                    toolForClickCreate === 'stage'
                                                    || toolForClickCreate === 'table'
                                                    || toolForClickCreate === 'zone'
                                                    || toolForClickCreate === 'wall'
                                                ) {
                                                    setRectPlacementDraft({
                                                        mode: 'block',
                                                        blockType: toolForClickCreate,
                                                        startX: pos.x,
                                                        startY: pos.y,
                                                        currentX: pos.x,
                                                        currentY: pos.y,
                                                    });
                                                } else {
                                                    addElement(toolForClickCreate, pos.x, pos.y);
                                                    setToolForClickCreate(null);
                                                }
                                            } else {
                                                const pos = getPointerPositionInStage();
                                                setSelectionRect({
                                                    startX: pos.x,
                                                    startY: pos.y,
                                                    currentX: pos.x,
                                                    currentY: pos.y,
                                                    appendToSelection: e.evt.shiftKey || e.evt.metaKey || e.evt.ctrlKey,
                                                });
                                            }
                                        }
                                    }}
                                    onMouseMove={() => {
                                        const pos = getPointerPositionInStage();
                                        setRectPlacementDraft((prev) => {
                                            if (!prev) {
                                                return null;
                                            }
                                            return {
                                                ...prev,
                                                currentX: pos.x,
                                                currentY: pos.y,
                                            };
                                        });
                                        setSelectionRect((prev) => (prev ? {
                                            ...prev,
                                            currentX: pos.x,
                                            currentY: pos.y,
                                        } : null));
                                    }}
                                    onMouseUp={() => {
                                        if (rectPlacementDraft) {
                                            if (rectPlacementDraft.mode === 'seats') {
                                                createSeatsInRect(rectPlacementDraft);
                                            } else {
                                                createBlockInRect(rectPlacementDraft);
                                            }
                                            setRectPlacementDraft(null);
                                            setToolForClickCreate(null);
                                            return;
                                        }
                                        if (!selectionRect || !normalizedSelectionRect) {
                                            return;
                                        }
                                        const tooSmall = normalizedSelectionRect.width < 4 || normalizedSelectionRect.height < 4;
                                        if (tooSmall) {
                                            if (!selectionRect.appendToSelection) {
                                                setSelectedIds([]);
                                            }
                                            setSelectionRect(null);
                                            return;
                                        }
                                        const selectedInRect = layout.filter((item) => {
                                            const intersectsRect = (
                                                a: { x: number; y: number; width: number; height: number },
                                                b: { x: number; y: number; width: number; height: number }
                                            ) => !(
                                                a.x > b.x + b.width
                                                || a.x + a.width < b.x
                                                || a.y > b.y + b.height
                                                || a.y + a.height < b.y
                                            );

                                            if (freezeSectorSelection && (item.type === 'sector' || item.type === 'zone')) {
                                                return false;
                                            }

                                            const node = nodeRefs.current[item._id];
                                            const stage = stageRef.current;
                                            if (!node || !stage) {
                                                return false;
                                            }
                                            // IMPORTANT: keep coordinates in Stage space (unscaled),
                                            // otherwise selection will drift under zoom.
                                            const bounds = node.getClientRect({
                                                relativeTo: stage,
                                                skipShadow: true,
                                                skipStroke: false,
                                            });
                                            return intersectsRect(
                                                normalizedSelectionRect,
                                                { x: bounds.x, y: bounds.y, width: bounds.width, height: bounds.height }
                                            );
                                        }).map((item) => item._id);

                                        if (selectionRect.appendToSelection) {
                                            setSelectedIds((prev) => Array.from(new Set([...prev, ...selectedInRect])));
                                        } else {
                                            setSelectedIds(selectedInRect);
                                        }
                                        setSelectionRect(null);
                                    }}
                                >
                                    <Layer listening={false}>
                                        {hallEditorGridLines}
                                    </Layer>
                                    <Layer>
                                        {/* sectors always behind other elements */}
                                        {layoutSectorsAndZones.map((item) => (
                                            <React.Fragment key={item._id}>
                                                {/* sector (interactive like a normal element) */}
                                                <Rect
                                                    ref={(node) => {
                                                        if (node) {
                                                            nodeRefs.current[item._id] = node;
                                                        }
                                                    }}
                                                    x={item.x}
                                                    y={item.y}
                                                    width={item.width}
                                                    height={item.height}
                                                    rotation={item.rotation}
                                                    fill={HALL_SECTOR.fill}
                                                    stroke={HALL_SECTOR.stroke}
                                                    strokeWidth={HALL_SECTOR.strokeWidth}
                                                    cornerRadius={getHallBlockCornerRadius(item.type as string)}
                                                    draggable
                                                    listening={!freezeSectorSelection}
                                                    onClick={(e) => {
                                                        const multi = e.evt.shiftKey || e.evt.metaKey || e.evt.ctrlKey;
                                                        if (multi) {
                                                            setSelectedIds((prev) => (
                                                                prev.includes(item._id)
                                                                    ? prev.filter((id) => id !== item._id)
                                                                    : [...prev, item._id]
                                                            ));
                                                            return;
                                                        }
                                                        setSelectedIds([item._id]);
                                                    }}
                                                    onDblClick={(e) => {
                                                        e.cancelBubble = true;
                                                        if (item.type === 'sector' || item.type === 'zone') {
                                                            openSectorSettings(item._id);
                                                        }
                                                    }}
                                                    onTap={() => setSelectedIds([item._id])}
                                                    onDragStart={(e) => {
                                                        const node = e.target;
                                                        singleDragStartRef.current = { id: item._id, x: node.x(), y: node.y() };
                                                        if (!selectedIds.includes(item._id)) {
                                                            setSelectedIds([item._id]);
                                                            return;
                                                        }
                                                        if (selectedIds.length < 2) {
                                                            return;
                                                        }
                                                        const startPositions: Record<string, { x: number; y: number }> = {};
                                                        selectedIds.forEach((id) => {
                                                            const nodeRef = nodeRefs.current[id];
                                                            if (nodeRef) {
                                                                startPositions[id] = { x: nodeRef.x(), y: nodeRef.y() };
                                                            }
                                                        });
                                                        // На всякий случай фиксируем старт активного узла из текущего ref.
                                                        const activeStart = startPositions[item._id] ?? { x: node.x(), y: node.y() };
                                                        startPositions[item._id] = activeStart;
                                                        dragGroupStartRef.current = startPositions;
                                                        dragGroupSelectedIdsRef.current = [...selectedIds];
                                                        const pointerPos = getPointerPositionInStage();
                                                        dragGroupPointerStartRef.current = pointerPos ? { x: pointerPos.x, y: pointerPos.y } : null;
                                                        activeDragRef.current = {
                                                            id: item._id,
                                                            startX: activeStart.x,
                                                            startY: activeStart.y,
                                                        };
                                                    }}
                                                    onDragMove={(e) => {
                                                        if (!activeDragRef.current || selectedIds.length < 2) {
                                                            return;
                                                        }
                                                        const activeNode = e.target;
                                                        const pointerPos = getPointerPositionInStage();
                                                        const pointerStart = dragGroupPointerStartRef.current;
                                                        if (!pointerPos || !pointerStart) {
                                                            return;
                                                        }
                                                        const dx = pointerPos.x - pointerStart.x;
                                                        const dy = pointerPos.y - pointerStart.y;

                                                        const activeId = activeDragRef.current.id;
                                                        dragGroupSelectedIdsRef.current.forEach((id) => {
                                                            if (id === activeId) {
                                                                return;
                                                            }
                                                            const nodeRef = nodeRefs.current[id];
                                                            const startPos = dragGroupStartRef.current[id];
                                                            if (nodeRef && startPos) {
                                                                nodeRef.x(startPos.x + dx);
                                                                nodeRef.y(startPos.y + dy);
                                                            }
                                                        });
                                                        activeNode.getLayer()?.batchDraw();
                                                    }}
                                                    onDragEnd={(e) => {
                                                        if (activeDragRef.current && selectedIds.length > 1) {
                                                            const startPositions = dragGroupStartRef.current;
                                                            const prevEls = layoutRef.current;
                                                            const proposed = prevEls.map((old) => {
                                                                const nodeRef = nodeRefs.current[old._id];
                                                                if (!selectedIds.includes(old._id) || !nodeRef) {
                                                                    return old;
                                                                }
                                                                return { ...old, x: nodeRef.x(), y: nodeRef.y() };
                                                            });
                                                            if (violatesNoSeatsSectorRule(proposed)) {
                                                                selectedIds.forEach((id) => {
                                                                    const nodeRef = nodeRefs.current[id];
                                                                    const start = startPositions[id];
                                                                    if (nodeRef && start) {
                                                                        nodeRef.x(start.x);
                                                                        nodeRef.y(start.y);
                                                                    }
                                                                });
                                                                e.target.getLayer()?.batchDraw();
                                                            } else if (!commitHallLayoutFromProposed(proposed)) {
                                                                selectedIds.forEach((id) => {
                                                                    const nodeRef = nodeRefs.current[id];
                                                                    const start = startPositions[id];
                                                                    if (nodeRef && start) {
                                                                        nodeRef.x(start.x);
                                                                        nodeRef.y(start.y);
                                                                    }
                                                                });
                                                                e.target.getLayer()?.batchDraw();
                                                            }
                                                            activeDragRef.current = null;
                                                            dragGroupStartRef.current = {};
                                                            dragGroupSelectedIdsRef.current = [];
                                                            dragGroupPointerStartRef.current = null;
                                                            return;
                                                        }
                                                        const start = singleDragStartRef.current?.id === item._id ? singleDragStartRef.current : null;
                                                        const prevElsSingle = layoutRef.current;
                                                        const proposedSingle = prevElsSingle.map((el) => (
                                                            el._id === item._id ? { ...el, x: e.target.x(), y: e.target.y() } : el
                                                        ));
                                                        if (violatesNoSeatsSectorRule(proposedSingle)) {
                                                            if (start) {
                                                                e.target.x(start.x);
                                                                e.target.y(start.y);
                                                                e.target.getLayer()?.batchDraw();
                                                            }
                                                            return;
                                                        }
                                                        if (!commitHallLayoutFromProposed(proposedSingle) && start) {
                                                            e.target.x(start.x);
                                                            e.target.y(start.y);
                                                            e.target.getLayer()?.batchDraw();
                                                        }
                                                    }}
                                                    onTransformEnd={(e) => {
                                                        const node = e.target;
                                                        const scaleX = node.scaleX();
                                                        const scaleY = node.scaleY();
                                                        const nextWidth = Math.max(14, node.width() * scaleX);
                                                        const nextHeight = Math.max(14, node.height() * scaleY);
                                                        node.scaleX(1);
                                                        node.scaleY(1);
                                                        const proposed = layoutRef.current.map((el) => (
                                                            el._id === item._id ? {
                                                                ...el,
                                                                x: node.x(),
                                                                y: node.y(),
                                                                rotation: node.rotation(),
                                                                width: nextWidth,
                                                                height: nextHeight,
                                                            } : el
                                                        ));
                                                        if (violatesNoSeatsSectorRule(proposed)) {
                                                            // revert via state (safe) by snapping node back to element values
                                                            const current = findInLayout(layout, item._id);
                                                            if (current) {
                                                                node.x(current.x);
                                                                node.y(current.y);
                                                                node.rotation(current.rotation);
                                                                node.width(current.width);
                                                                node.height(current.height);
                                                                node.getLayer()?.batchDraw();
                                                            }
                                                            return;
                                                        }
                                                        updateElement(item._id, (old) => ({ ...old, x: node.x(), y: node.y(), rotation: node.rotation(), width: nextWidth, height: nextHeight }));
                                                    }}
                                                />
                                                <Text
                                                    x={item.x}
                                                    y={item.type === 'zone' ? (item.y + item.height / 2 - 8) : (item.y - 22)}
                                                    width={item.type === 'zone' ? item.width : item.width}
                                                    align={item.type === 'zone' ? 'center' : 'center'}
                                                    text={item.label}
                                                    fontSize={item.type === 'zone' ? 16 : 14}
                                                    fill={item.type === 'zone' ? HALL_SECTOR.labelNoSeats : HALL_SECTOR.labelRegular}
                                                    listening={false}
                                                />
                                            </React.Fragment>
                                        ))}
                                    </Layer>

                                    <Layer>
                                        {layoutBlocks.map((item) => {
                                            const seatLabel = item.type === 'seat'
                                                ? (seatSmartLabels.seatLabelsById[item._id] ?? '')
                                                : '';
                                            const { w: seatW, h: seatH, halfW: seatHalfW, halfH: seatHalfH } = item.type === 'seat'
                                                ? seatSize(item)
                                                : { w: 0, h: 0, halfW: 0, halfH: 0 };
                                            const sideLabels = item.type === 'seat'
                                                ? seatSmartLabels.sideRowLabelsBySeatId[item._id]
                                                : undefined;
                                            const rowLabelFontSize = hallSeatRowLabelFontSizeForRect(seatW, seatH);
                                            const rowLabelGap = Math.max(6, Math.round(seatW * 0.18));
                                            const leftRowLabelWidth = sideLabels?.left
                                                ? Math.max(16, Math.round(sideLabels.left.length * rowLabelFontSize * 0.72) + 4)
                                                : 0;
                                            const rightRowLabelWidth = sideLabels?.right
                                                ? Math.max(16, Math.round(sideLabels.right.length * rowLabelFontSize * 0.72) + 4)
                                                : 0;
                                            const shapeCommon = {
                                                id: item._id,
                                                ref: (node: Konva.Shape | null) => {
                                                    if (node) {
                                                        nodeRefs.current[item._id] = node;
                                                    }
                                                },
                                                fill: resolveHallElementFill(item),
                                                draggable: true as const,
                                                rotation: item.rotation ?? 0,
                                                onClick: (e: Konva.KonvaEventObject<MouseEvent>) => {
                                                    const multi = e.evt.shiftKey || e.evt.metaKey || e.evt.ctrlKey;
                                                    if (multi) {
                                                        setSelectedIds((prev) => (
                                                            prev.includes(item._id)
                                                                ? prev.filter((id) => id !== item._id)
                                                                : [...prev, item._id]
                                                        ));
                                                        return;
                                                    }
                                                    setSelectedIds([item._id]);
                                                },
                                                onDblClick: (e: Konva.KonvaEventObject<MouseEvent>) => {
                                                    e.cancelBubble = true;
                                                    if (item.type === 'seat') {
                                                        openSeatSettings(item._id);
                                                    } else if (item.type === 'stage') {
                                                        openStageSettings(item._id);
                                                    }
                                                },
                                                onTap: () => setSelectedIds([item._id]),
                                                onDragStart: (e: Konva.KonvaEventObject<DragEvent>) => {
                                                    const node = e.target;
                                                    singleDragStartRef.current = { id: item._id, x: node.x(), y: node.y() };
                                                    if (!selectedIds.includes(item._id)) {
                                                        setSelectedIds([item._id]);
                                                        return;
                                                    }
                                                    if (selectedIds.length < 2) {
                                                        return;
                                                    }
                                                    const startPositions: Record<string, { x: number; y: number }> = {};
                                                    selectedIds.forEach((id) => {
                                                        const nodeRef = nodeRefs.current[id];
                                                        if (nodeRef) {
                                                            startPositions[id] = { x: nodeRef.x(), y: nodeRef.y() };
                                                        }
                                                    });
                                                    const activeStart = startPositions[item._id] ?? { x: node.x(), y: node.y() };
                                                    startPositions[item._id] = activeStart;
                                                    dragGroupStartRef.current = startPositions;
                                                    dragGroupSelectedIdsRef.current = [...selectedIds];
                                                    const pointerPos = getPointerPositionInStage();
                                                    dragGroupPointerStartRef.current = pointerPos ? { x: pointerPos.x, y: pointerPos.y } : null;
                                                    activeDragRef.current = {
                                                        id: item._id,
                                                        startX: activeStart.x,
                                                        startY: activeStart.y,
                                                    };
                                                },
                                                onDragMove: (e: Konva.KonvaEventObject<DragEvent>) => {
                                                    if (!activeDragRef.current || selectedIds.length < 2) {
                                                        return;
                                                    }
                                                    const activeNode = e.target;
                                                    const pointerPos = getPointerPositionInStage();
                                                    const pointerStart = dragGroupPointerStartRef.current;
                                                    if (!pointerPos || !pointerStart) {
                                                        return;
                                                    }
                                                    const dx = pointerPos.x - pointerStart.x;
                                                    const dy = pointerPos.y - pointerStart.y;

                                                    const activeId = activeDragRef.current.id;
                                                    dragGroupSelectedIdsRef.current.forEach((id) => {
                                                        if (id === activeId) {
                                                            return;
                                                        }
                                                        const nodeRef = nodeRefs.current[id];
                                                        const startPos = dragGroupStartRef.current[id];
                                                        if (nodeRef && startPos) {
                                                            nodeRef.x(startPos.x + dx);
                                                            nodeRef.y(startPos.y + dy);
                                                        }
                                                    });
                                                    activeNode.getLayer()?.batchDraw();
                                                },
                                                onDragEnd: (e: Konva.KonvaEventObject<DragEvent>) => {
                                                    if (activeDragRef.current && selectedIds.length > 1) {
                                                        const startPositions = dragGroupStartRef.current;
                                                        const prevEls = layoutRef.current;
                                                        const proposed = prevEls.map((old) => {
                                                            const nodeRef = nodeRefs.current[old._id];
                                                            if (!selectedIds.includes(old._id) || !nodeRef) {
                                                                return old;
                                                            }
                                                            return { ...old, x: nodeRef.x(), y: nodeRef.y() };
                                                        });
                                                        if (violatesNoSeatsSectorRule(proposed)) {
                                                            selectedIds.forEach((id) => {
                                                                const nodeRef = nodeRefs.current[id];
                                                                const start = startPositions[id];
                                                                if (nodeRef && start) {
                                                                    nodeRef.x(start.x);
                                                                    nodeRef.y(start.y);
                                                                }
                                                            });
                                                            e.target.getLayer()?.batchDraw();
                                                        } else if (!commitHallLayoutFromProposed(proposed)) {
                                                            selectedIds.forEach((id) => {
                                                                const nodeRef = nodeRefs.current[id];
                                                                const start = startPositions[id];
                                                                if (nodeRef && start) {
                                                                    nodeRef.x(start.x);
                                                                    nodeRef.y(start.y);
                                                                }
                                                            });
                                                            e.target.getLayer()?.batchDraw();
                                                        }
                                                        activeDragRef.current = null;
                                                        dragGroupStartRef.current = {};
                                                        dragGroupSelectedIdsRef.current = [];
                                                        dragGroupPointerStartRef.current = null;
                                                        return;
                                                    }
                                                    const start = singleDragStartRef.current?.id === item._id ? singleDragStartRef.current : null;
                                                    const prevElsSingle = layoutRef.current;
                                                    const proposedSingle = prevElsSingle.map((el) => (
                                                        el._id === item._id ? { ...el, x: e.target.x(), y: e.target.y() } : el
                                                    ));
                                                    if (violatesNoSeatsSectorRule(proposedSingle)) {
                                                        if (start) {
                                                            e.target.x(start.x);
                                                            e.target.y(start.y);
                                                            e.target.getLayer()?.batchDraw();
                                                        }
                                                        return;
                                                    }
                                                    if (!commitHallLayoutFromProposed(proposedSingle) && start) {
                                                        e.target.x(start.x);
                                                        e.target.y(start.y);
                                                        e.target.getLayer()?.batchDraw();
                                                    }
                                                },
                                                onTransformEnd: (e: Konva.KonvaEventObject<Event>) => {
                                                    if (selectedIds.length > 1) {
                                                        return;
                                                    }
                                                    const node = e.target;
                                                    if (item.type === 'seat') {
                                                        const scaleX = node.scaleX();
                                                        const scaleY = node.scaleY();
                                                        const nextWidth = Math.max(8, node.width() * scaleX);
                                                        const nextHeight = Math.max(8, node.height() * scaleY);
                                                        const nextX = node.x();
                                                        const nextY = node.y();

                                                        node.scaleX(1);
                                                        node.scaleY(1);
                                                        (node as any).width?.(nextWidth);
                                                        (node as any).height?.(nextHeight);
                                                        (node as any).offsetX?.(nextWidth / 2);
                                                        (node as any).offsetY?.(nextHeight / 2);
                                                        node.x(nextX);
                                                        node.y(nextY);
                                                        updateElement(item._id, (old) => ({
                                                            ...old,
                                                            x: nextX,
                                                            y: nextY,
                                                            rotation: node.rotation(),
                                                            width: nextWidth,
                                                            height: nextHeight,
                                                        }));
                                                        return;
                                                    }
                                                    const scaleX = node.scaleX();
                                                    const scaleY = node.scaleY();
                                                    const nextWidth = Math.max(14, node.width() * scaleX);
                                                    const nextHeight = Math.max(14, node.height() * scaleY);
                                                    node.scaleX(1);
                                                    node.scaleY(1);
                                                    updateElement(item._id, (old) => ({
                                                        ...old,
                                                        x: node.x(),
                                                        y: node.y(),
                                                        rotation: node.rotation(),
                                                        width: nextWidth,
                                                        height: nextHeight,
                                                    }));
                                                },
                                            };
                                            return (
                                                <React.Fragment key={item._id}>
                                                    {item.type === 'seat' ? (
                                                        <Rect
                                                            {...shapeCommon}
                                                            x={item.x}
                                                            y={item.y}
                                                            offsetX={seatHalfW}
                                                            offsetY={seatHalfH}
                                                            width={seatW}
                                                            height={seatH}
                                                            cornerRadius={getHallSeatCornerRadius(seatW, seatH)}
                                                        />
                                                    ) : (
                                                        <Rect
                                                            {...shapeCommon}
                                                            x={item.x}
                                                            y={item.y}
                                                            width={item.width}
                                                            height={item.height}
                                                            cornerRadius={getHallBlockCornerRadius(item.type as string)}
                                                        />
                                                    )}
                                                    {item.type === 'seat' && (
                                                        <>
                                                            <Text
                                                                x={item.x}
                                                                y={item.y}
                                                                offsetX={seatHalfW}
                                                                offsetY={seatHalfH}
                                                                width={seatW}
                                                                height={seatH}
                                                                rotation={item.rotation ?? 0}
                                                                align="center"
                                                                verticalAlign="middle"
                                                                wrap="none"
                                                                text={seatLabel}
                                                                fontSize={hallSeatLabelFontSizeForRect(
                                                                    seatLabel,
                                                                    seatW,
                                                                    seatH,
                                                                )}
                                                                fontStyle="bold"
                                                                fontFamily="Arial,sans-serif"
                                                                fill={hallSeatLabelFill(item)}
                                                                listening={false}
                                                            />
                                                            {sideLabels?.left && (
                                                                <Text
                                                                    x={item.x - seatHalfW - rowLabelGap - leftRowLabelWidth / 2}
                                                                    y={item.y}
                                                                    offsetX={leftRowLabelWidth / 2}
                                                                    offsetY={seatH / 2}
                                                                    width={leftRowLabelWidth}
                                                                    height={seatH}
                                                                    rotation={item.rotation ?? 0}
                                                                    align="right"
                                                                    verticalAlign="middle"
                                                                    wrap="none"
                                                                    text={sideLabels.left}
                                                                    fontSize={rowLabelFontSize}
                                                                    fontStyle="bold"
                                                                    fontFamily="Arial,sans-serif"
                                                                    fill={hallSeatRowLabelFill()}
                                                                    listening={false}
                                                                />
                                                            )}
                                                            {sideLabels?.right && (
                                                                <Text
                                                                    x={item.x + seatHalfW + rowLabelGap + rightRowLabelWidth / 2}
                                                                    y={item.y}
                                                                    offsetX={rightRowLabelWidth / 2}
                                                                    offsetY={seatH / 2}
                                                                    width={rightRowLabelWidth}
                                                                    height={seatH}
                                                                    rotation={item.rotation ?? 0}
                                                                    align="left"
                                                                    verticalAlign="middle"
                                                                    wrap="none"
                                                                    text={sideLabels.right}
                                                                    fontSize={rowLabelFontSize}
                                                                    fontStyle="bold"
                                                                    fontFamily="Arial,sans-serif"
                                                                    fill={hallSeatRowLabelFill()}
                                                                    listening={false}
                                                                />
                                                            )}
                                                        </>
                                                    )}
                                                    {item.type !== 'seat' && item.type !== 'stage' && (
                                                        <Text
                                                            x={item.x + 6}
                                                            y={item.y + 6}
                                                            text={item.type === 'wall' ? '' : item.label}
                                                            fontSize={12}
                                                            fill={getHallBlockOnShapeTextFill(item.type as string)}
                                                            listening={false}
                                                        />
                                                    )}
                                                    <Text
                                                        x={item.x}
                                                        y={item.y + item.height / 2 - 8}
                                                        width={item.width}
                                                        align="center"
                                                        text={item.type === 'stage' ? item.label : ''}
                                                        fontSize={16}
                                                        fill={getHallBlockOnShapeTextFill('stage')}
                                                        listening={false}
                                                        visible={item.type === 'stage'}
                                                    />
                                                </React.Fragment>
                                            );
                                        })}
                                        {seatDraftStats && (
                                            <>
                                                <Rect
                                                    x={seatDraftStats.x}
                                                    y={seatDraftStats.y}
                                                    width={seatDraftStats.width}
                                                    height={seatDraftStats.height}
                                                    fill={HALL_SEAT_DRAFT.rectFill}
                                                    stroke={HALL_SEAT_DRAFT.rectStroke}
                                                    dash={[6, 6]}
                                                    listening={false}
                                                />
                                                {seatDraftPreviewSeats.map((previewSeat, idx) => (
                                                    <Rect
                                                        key={`seat-preview-${idx}`}
                                                        x={previewSeat.x}
                                                        y={previewSeat.y}
                                                        offsetX={previewSeat.halfW}
                                                        offsetY={previewSeat.halfH}
                                                        width={previewSeat.w}
                                                        height={previewSeat.h}
                                                        cornerRadius={getHallSeatCornerRadius(previewSeat.w, previewSeat.h)}
                                                        fill={HALL_SEAT_DRAFT.previewFill}
                                                        stroke={HALL_SEAT_DRAFT.previewStroke}
                                                        strokeWidth={1}
                                                        listening={false}
                                                    />
                                                ))}
                                                <Rect
                                                    x={seatDraftStats.x + 6}
                                                    y={seatDraftStats.y + 6}
                                                    width={220}
                                                    height={38}
                                                    cornerRadius={6}
                                                    fill={HALL_SEAT_DRAFT.statsBg}
                                                    listening={false}
                                                />
                                                <Text
                                                    x={seatDraftStats.x + 12}
                                                    y={seatDraftStats.y + 13}
                                                    text={`${Lang.key('hall_seat_draft_rows')}: ${seatDraftStats.rows}  ${Lang.key('hall_seat_draft_seats_in_line')}: ${seatDraftStats.cols}  ${Lang.key('hall_seat_draft_total')}: ${seatDraftStats.total}`}
                                                    fontSize={12}
                                                    fill={HALL_SEAT_DRAFT.statsText}
                                                    listening={false}
                                                />
                                            </>
                                        )}
                                        {blockPlacementPreview && (
                                            <>
                                                <Rect
                                                    x={blockPlacementPreview.x}
                                                    y={blockPlacementPreview.y}
                                                    width={blockPlacementPreview.width}
                                                    height={blockPlacementPreview.height}
                                                    fill={HALL_SEAT_DRAFT.rectFill}
                                                    stroke={HALL_SEAT_DRAFT.rectStroke}
                                                    dash={[6, 6]}
                                                    listening={false}
                                                />
                                                <Text
                                                    x={blockPlacementPreview.x + 8}
                                                    y={blockPlacementPreview.y + 8}
                                                    text={`${Math.round(blockPlacementPreview.width)} × ${Math.round(blockPlacementPreview.height)}`}
                                                    fontSize={12}
                                                    fill={HALL_SEAT_DRAFT.statsText}
                                                    listening={false}
                                                />
                                            </>
                                        )}
                                        {normalizedSelectionRect && (
                                            <Rect
                                                x={normalizedSelectionRect.x}
                                                y={normalizedSelectionRect.y}
                                                width={normalizedSelectionRect.width}
                                                height={normalizedSelectionRect.height}
                                                fill={HALL_SELECTION.rectFill}
                                                stroke={HALL_SELECTION.rectStroke}
                                                dash={[4, 4]}
                                                listening={false}
                                            />
                                        )}

                                        {/* Drag by visible selection frame (multi-select) */}
                                        {selectionBounds && !rectPlacementDraft && !selectionRect && (
                                            <Rect
                                                x={selectionBounds.x}
                                                y={selectionBounds.y}
                                                width={selectionBounds.width}
                                                height={selectionBounds.height}
                                                stroke={HALL_SELECTION.frameStroke}
                                                dash={[6, 4]}
                                                strokeWidth={2}
                                                fill={HALL_SELECTION.frameFill}
                                                draggable
                                                onDragStart={() => {
                                                    const pointerPos = getPointerPositionInStage();
                                                    selectionFramePointerStartRef.current = pointerPos ? { x: pointerPos.x, y: pointerPos.y } : null;

                                                    const startPositions: Record<string, { x: number; y: number }> = {};
                                                    selectedIds.forEach((id) => {
                                                        const nodeRef = nodeRefs.current[id];
                                                        if (nodeRef) {
                                                            startPositions[id] = { x: nodeRef.x(), y: nodeRef.y() };
                                                        }
                                                    });
                                                    selectionFrameDragStartRef.current = startPositions;
                                                }}
                                                onDragMove={(e) => {
                                                    const pointerPos = getPointerPositionInStage();
                                                    const pointerStart = selectionFramePointerStartRef.current;
                                                    if (!pointerPos || !pointerStart) {
                                                        return;
                                                    }
                                                    const dx = pointerPos.x - pointerStart.x;
                                                    const dy = pointerPos.y - pointerStart.y;

                                                    selectedIds.forEach((id) => {
                                                        const nodeRef = nodeRefs.current[id];
                                                        const startPos = selectionFrameDragStartRef.current[id];
                                                        if (nodeRef && startPos) {
                                                            nodeRef.x(startPos.x + dx);
                                                            nodeRef.y(startPos.y + dy);
                                                        }
                                                    });
                                                    e.target.getLayer()?.batchDraw();
                                                }}
                                                onDragEnd={() => {
                                                    const startPositions = selectionFrameDragStartRef.current;
                                                    const prevEls = layoutRef.current;
                                                    const proposed = prevEls.map((old) => {
                                                        const nodeRef = nodeRefs.current[old._id];
                                                        if (!selectedIds.includes(old._id) || !nodeRef) {
                                                            return old;
                                                        }
                                                        return { ...old, x: nodeRef.x(), y: nodeRef.y() };
                                                    });
                                                    if (violatesNoSeatsSectorRule(proposed)) {
                                                        selectedIds.forEach((id) => {
                                                            const nodeRef = nodeRefs.current[id];
                                                            const start = startPositions[id];
                                                            if (nodeRef && start) {
                                                                nodeRef.x(start.x);
                                                                nodeRef.y(start.y);
                                                            }
                                                        });
                                                        stageRef.current?.getLayers?.()?.forEach((l: any) => l.batchDraw());
                                                    } else if (!commitHallLayoutFromProposed(proposed)) {
                                                        selectedIds.forEach((id) => {
                                                            const nodeRef = nodeRefs.current[id];
                                                            const start = startPositions[id];
                                                            if (nodeRef && start) {
                                                                nodeRef.x(start.x);
                                                                nodeRef.y(start.y);
                                                            }
                                                        });
                                                        stageRef.current?.getLayers?.()?.forEach((l: any) => l.batchDraw());
                                                    }
                                                    selectionFrameDragStartRef.current = {};
                                                    selectionFramePointerStartRef.current = null;
                                                }}
                                            />
                                        )}
                                        {hallCheckFlash?.ids.map((id) => {
                                            const el = findInLayout(layout, id);
                                            if (!el) {
                                                return null;
                                            }
                                            const o = hallCheckOverlayOpacity(hallCheckPulseMs);
                                            if (el.type === 'seat') {
                                                const { w, h, halfW, halfH } = seatSize(el);
                                                return (
                                                    <Rect
                                                        key={`hall-check-${id}`}
                                                        x={el.x}
                                                        y={el.y}
                                                        offsetX={halfW}
                                                        offsetY={halfH}
                                                        width={w}
                                                        height={h}
                                                        rotation={el.rotation ?? 0}
                                                        stroke={HALL_DUPLICATE_HIGHLIGHT_STROKE}
                                                        strokeWidth={5}
                                                        fillEnabled={false}
                                                        cornerRadius={getHallSeatCornerRadius(w, h)}
                                                        opacity={o}
                                                        listening={false}
                                                    />
                                                );
                                            }
                                            return (
                                                <Rect
                                                    key={`hall-check-${id}`}
                                                    x={el.x}
                                                    y={el.y}
                                                    width={el.width}
                                                    height={el.height}
                                                    rotation={el.rotation}
                                                    cornerRadius={getHallBlockCornerRadius(el.type as string)}
                                                    stroke={HALL_DUPLICATE_HIGHLIGHT_STROKE}
                                                    strokeWidth={5}
                                                    fillEnabled={false}
                                                    opacity={o}
                                                    listening={false}
                                                />
                                            );
                                        })}
                                        <Transformer
                                            ref={trRef}
                                            rotateEnabled
                                            enabledAnchors={[
                                                'top-left',
                                                'top-center',
                                                'top-right',
                                                'middle-right',
                                                'bottom-right',
                                                'bottom-center',
                                                'bottom-left',
                                                'middle-left',
                                            ]}
                                            boundBoxFunc={(_, newBox) => {
                                                if (newBox.width < 14 || newBox.height < 14) {
                                                    return _;
                                                }
                                                return newBox;
                                            }}
                                            onTransformEnd={() => {
                                                const tr = trRef.current;
                                                if (!tr) {
                                                    return;
                                                }
                                                const nodes = tr.nodes?.() ?? [];
                                                if (nodes.length <= 1) {
                                                    return;
                                                }
                                                const ids = nodes
                                                    .map((n: any) => (typeof n.id === 'function' ? String(n.id()) : ''))
                                                    .filter(Boolean);
                                                if (!ids.length) {
                                                    return;
                                                }
                                                patchLayout((prev) => prev.map((old) => {
                                                    if (!ids.includes(old._id)) {
                                                        return old;
                                                    }
                                                    const node: any = nodes.find((n: any) => (typeof n.id === 'function' ? String(n.id()) : '') === old._id);
                                                    if (!node) {
                                                        return old;
                                                    }
                                                    const scaleX = node.scaleX();
                                                    const scaleY = node.scaleY();
                                                    const w0 = node.width();
                                                    const h0 = node.height();
                                                    const nextWidth = Math.max(old.type === 'seat' ? 8 : 14, w0 * scaleX);
                                                    const nextHeight = Math.max(old.type === 'seat' ? 8 : 14, h0 * scaleY);
                                                    const seatW = nextWidth;
                                                    const seatH = nextHeight;
                                                    const seatX = node.x();
                                                    const seatY = node.y();

                                                    node.scaleX(1);
                                                    node.scaleY(1);
                                                    // Keep Konva node visually consistent after reset.
                                                    (node as any).width?.(old.type === 'seat' ? seatW : nextWidth);
                                                    (node as any).height?.(old.type === 'seat' ? seatH : nextHeight);
                                                    if (old.type === 'seat') {
                                                        (node as any).offsetX?.(seatW / 2);
                                                        (node as any).offsetY?.(seatH / 2);
                                                        node.x(seatX);
                                                        node.y(seatY);
                                                    }
                                                    return {
                                                        ...old,
                                                        x: old.type === 'seat' ? seatX : node.x(),
                                                        y: old.type === 'seat' ? seatY : node.y(),
                                                        rotation: node.rotation(),
                                                        width: old.type === 'seat' ? seatW : nextWidth,
                                                        height: old.type === 'seat' ? seatH : nextHeight,
                                                    };
                                                }), true);
                                            }}
                                        />
                                    </Layer>
                                </Stage>
                            </div>
                        </div>
                    </GridRow>



                </GridCol>
            </Grid>}



            <SeatNumberingDialog
                open={seatNumberingOpen}
                onOpenChange={setSeatNumberingOpen}
                startFrom={seatNumberStart}
                fillDirection={seatNumberFillDirection}
                onStartFromChange={setSeatNumberStart}
                onFillDirectionChange={setSeatNumberFillDirection}
                onApply={applySeatNumbering}
            />
            <SeatSettingsDialog
                open={seatSettingsOpen}
                onOpenChange={setSeatSettingsOpen}
                seatNumber={seatSettingsSeatNumber}
                onSeatNumberChange={setSeatSettingsSeatNumber}
                seatRow={seatSettingsSeatRow}
                onSeatRowChange={setSeatSettingsSeatRow}
                onApply={saveSeatSettings}
            />
            <SectorSettingsDialog
                open={sectorSettingsOpen}
                onOpenChange={setSectorSettingsOpen}
                name={sectorSettingsName}
                onNameChange={setSectorSettingsName}
                onApply={saveSectorSettings}
            />
            <StageSettingsDialog
                open={stageSettingsOpen}
                onOpenChange={setStageSettingsOpen}
                name={stageSettingsName}
                onNameChange={setStageSettingsName}
                onApply={saveStageSettings}
            />
            <SeatRowsDialog
                open={seatRowsOpen}
                onOpenChange={setSeatRowsOpen}
                startFrom={seatRowStart}
                fillDirection={seatRowFillDirection}
                onStartFromChange={setSeatRowStart}
                onFillDirectionChange={setSeatRowFillDirection}
                onApply={applySeatRows}
            />


            <DialogFooter>
                <Button onClick={() => {
                    setOpen(false);
                }}>
                    {Lang.key('cancel')}
                </Button>
                <Button onClick={() => {

                    setOpen(false);
                }}>
                    {Lang.key('save')}
                </Button>
            </DialogFooter>
        </DialogContent>

    </Dialog>

});

export default DialogHallEditor;