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

import _ from 'lodash';
import {
    ChevronDown,
    ChevronLeft,
    ChevronRight,
    ChevronUp,
} from 'lucide-react';

import { rpc } from '@/api/request';
import {
    HallElementType,
    HallSelectorElementType,
} from '@/api/types';
import { Badge } from '@/components/ui/badge';
import { Skeleton } from '@/components/ui/skeleton';
import { hallSectorLabelLayout } from '@/componentsRoot/DialogHallEditor/hallSectorLabel';
import {
    hallPolygonAbsolutePoints,
    hallPolygonBBox,
    hallPolygonLabelLayout,
    pointInHallPolygon,
    type HallPolygonPoint,
} from '@/componentsRoot/DialogHallEditor/hallPolygon';
import { resolveHallCanvasSize } from '@/componentsRoot/DialogHallEditor/hallFlat';

const HALL_SEAT_SIZE = 28;
const HALL_KONVA_FULL_ROUND_RECT_RADIUS = 6;

type HallBlockVisualConfig = {
    fill?: string;
    cornerRadius: number;
    textOnShape: string;
    neutralFill?: string;
    seatRowText?: string;
    scheme?: {
        rectFill: string;
        stroke: string;
        strokeWidth: number;
        nameText: string;
    };
};

const HALL_BLOCK_LAYOUT_DEFAULTS: Record<
    string,
    { width: number; height: number; label: string }
> = {
    seat: { width: 28, height: 28, label: 'Места' },
    table: { width: 64, height: 64, label: 'Стол' },
    stage: { width: 240, height: 110, label: 'Сцена' },
    wall: { width: 180, height: 18, label: 'Стена/Разделитель' },
    sector: { width: 260, height: 160, label: 'Сектор' },
    sector_polygon: { width: 260, height: 160, label: 'Сектор (полигон)' },
    zone: { width: 260, height: 160, label: 'Зона (входной билет, без места)' },
};

const HALL_BLOCK_CONFIG: Record<string, HallBlockVisualConfig> = {
    seat: {
        neutralFill: '#94a3b8',
        cornerRadius: HALL_KONVA_FULL_ROUND_RECT_RADIUS,
        seatRowText: '#000000',
        textOnShape: '#000000',
    },
    table: {
        fill: '#8b5cf6',
        cornerRadius: HALL_KONVA_FULL_ROUND_RECT_RADIUS,
        textOnShape: '#ffffff',
    },
    stage: {
        fill: '#F5F5F5',
        cornerRadius: 8,
        textOnShape: '#90A4AE',
    },
    wall: {
        fill: '#EEEEEE',
        cornerRadius: 2,
        textOnShape: '#ffffff',
    },
    sector: {
        fill: '#FAFAFA',
        cornerRadius: 6,
        textOnShape: '#90A4AE',
        scheme: {
            rectFill: '#ffffff',
            stroke: '#EEEEEE',
            strokeWidth: 2,
            nameText: '#94a3b8',
        },
    },
    sector_polygon: {
        fill: '#FAFAFA',
        cornerRadius: 0,
        textOnShape: '#90A4AE',
        scheme: {
            rectFill: '#ffffff',
            stroke: '#EEEEEE',
            strokeWidth: 2,
            nameText: '#94a3b8',
        },
    },
    zone: {
        fill: '#9E9E9E',
        cornerRadius: 6,
        textOnShape: '#90A4AE',
        scheme: {
            rectFill: '#ffffff',
            stroke: '#EEEEEE',
            strokeWidth: 2,
            nameText: '#475569',
        },
    },
};

const HALL_SECTOR = {
    fill: HALL_BLOCK_CONFIG.sector.scheme!.rectFill,
    stroke: HALL_BLOCK_CONFIG.sector.scheme!.stroke,
    strokeWidth: HALL_BLOCK_CONFIG.sector.scheme!.strokeWidth,
    labelRegular: HALL_BLOCK_CONFIG.sector.scheme!.nameText,
    labelNoSeats: HALL_BLOCK_CONFIG.zone.scheme!.nameText,
};



const HALL_ELEMENT_DEFAULTS: Record<
    string,
    { type: string; width: number; height: number; fill: string; label: string }
> = (() => {
    const out: Record<
        string,
        { type: string; width: number; height: number; fill: string; label: string }
    > = {};
    for (const t of Object.keys(HALL_BLOCK_CONFIG)) {
        const layout = HALL_BLOCK_LAYOUT_DEFAULTS[t];
        const fill =
            t === 'seat'
                ? HALL_BLOCK_CONFIG.seat.neutralFill!
                : (HALL_BLOCK_CONFIG[t] as { fill: string }).fill;
        out[t] = {
            type: t,
            width: layout.width,
            height: layout.height,
            label: layout.label,
            fill,
        };
    }
    return out;
})();

// ——— Хелперы темы ———

function parseHexRgb(hex: string): { r: number; g: number; b: number } | null {
    const h = hex.trim().replace(/^#/, '');
    if (h.length === 3) {
        const r = parseInt(h[0] + h[0], 16);
        const g = parseInt(h[1] + h[1], 16);
        const b = parseInt(h[2] + h[2], 16);
        return Number.isFinite(r) && Number.isFinite(g) && Number.isFinite(b)
            ? { r, g, b }
            : null;
    }
    if (h.length === 6) {
        const n = parseInt(h, 16);
        if (!Number.isFinite(n)) return null;
        return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 };
    }
    return null;
}

function hallRelativeLuminance(rgb: { r: number; g: number; b: number }): number {
    const lin = (c: number) => {
        const s = Math.max(0, Math.min(255, c)) / 255;
        return s <= 0.04045 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
    };
    return 0.2126 * lin(rgb.r) + 0.7152 * lin(rgb.g) + 0.0722 * lin(rgb.b);
}

function hallContrastTextOnFill(backgroundHex: string): '#ffffff' | '#000000' {
    const rgb = parseHexRgb(backgroundHex);
    if (!rgb) return '#000000';
    const L = hallRelativeLuminance(rgb);
    const whiteOnBg = (1 + 0.05) / (L + 0.05);
    const blackOnBg = (L + 0.05) / (0 + 0.05);
    return whiteOnBg >= blackOnBg ? '#ffffff' : '#000000';
}

function resolveHallElementFill(element: any): string {
    const t = element?.type;
    const neutral = HALL_BLOCK_CONFIG.seat.neutralFill!;
    if (!t || !(t in HALL_ELEMENT_DEFAULTS)) return neutral;
    if (t === 'seat') return neutral;
    return HALL_ELEMENT_DEFAULTS[t].fill;
}

const hallSeatApiLabelFill = (): string =>
    hallContrastTextOnFill(HALL_BLOCK_CONFIG.seat.neutralFill!);

const hallSeatRowLabelFill = (): string =>
    HALL_BLOCK_CONFIG.seat.seatRowText ?? '#000000';

function getHallBlockCornerRadius(type: string | undefined): number {
    if (!type || !(type in HALL_BLOCK_CONFIG)) return 0;
    return HALL_BLOCK_CONFIG[type].cornerRadius;
}

/** Полное скругление (100%): радиус ≥ половины меньшей стороны → круг / капсула. */
function getHallSeatCornerRadius(width: number, height: number): number {
    const w =
        Number.isFinite(width) && width > 0 ? width : HALL_BLOCK_LAYOUT_DEFAULTS.seat.width;
    const h =
        Number.isFinite(height) && height > 0 ? height : HALL_BLOCK_LAYOUT_DEFAULTS.seat.height;
    return Math.min(w, h) / 2;
}

function getHallBlockOnShapeTextFill(type: string | undefined): string {
    if (!type || !(type in HALL_BLOCK_CONFIG)) return '#ffffff';
    return HALL_BLOCK_CONFIG[type].textOnShape;
}

// ——— Размер шрифта, вписанного в прямоугольник (без canvas) ———

const SEAT_FONT_AVG_CHAR_WIDTH = 0.58;

function hallSeatLabelFontSizeForRect(
    text: string,
    width: number,
    height: number,
): number {
    const w = Number.isFinite(width) && width > 0 ? width : HALL_SEAT_SIZE;
    const h = Number.isFinite(height) && height > 0 ? height : HALL_SEAT_SIZE;
    const padW = 0.86;
    const padH = 0.74;
    const maxW = Math.max(4, w * padW);
    const maxH = Math.max(4, h * padH);
    const maxFont = Math.min(32, Math.floor(Math.min(w, h) * 0.92));
    const minFont = 5;
    const textLen = Math.max(1, text.length);
    const fontByWidth = Math.floor(maxW / (textLen * SEAT_FONT_AVG_CHAR_WIDTH));
    const fontByHeight = Math.floor(maxH);
    return Math.max(minFont, Math.min(maxFont, Math.min(fontByWidth, fontByHeight)));
}

function hallSeatRowLabelFontSizeForRect(width: number, height: number): number {
    const w = Number.isFinite(width) && width > 0 ? width : HALL_SEAT_SIZE;
    const h = Number.isFinite(height) && height > 0 ? height : HALL_SEAT_SIZE;
    return Math.max(10, Math.min(18, Math.round(Math.min(w, h) * 0.58)));
}

// ——— Умные подписи мест ———

type HallSeatSmartSideLabels = { left?: string; right?: string };

function normalizeHallSeatLabelValue(value: string | null | undefined): string | null {
    if (value == null) return null;
    const normalized = String(value).trim();
    return normalized !== '' ? normalized : null;
}

function formatHallSeatRowSlashNumber(
    seatRow: string | null | undefined,
    seatNumber: string | null | undefined,
): string {
    const row =
        seatRow != null && String(seatRow).trim() !== ''
            ? String(seatRow).trim()
            : '*';
    const num =
        seatNumber != null && String(seatNumber).trim() !== ''
            ? String(seatNumber).trim()
            : '*';
    return `${row}/${num}`;
}

function formatHallSeatNumberOnly(seatNumber: string | null | undefined): string {
    return normalizeHallSeatLabelValue(seatNumber) ?? '*';
}

function buildHallSeatSmartLabels(
    seats: any[],
    rowTolerance = Math.max(8, Math.round(HALL_SEAT_SIZE * 0.6)),
): {
    seatLabelsById: Record<string, string>;
    sideRowLabelsBySeatId: Record<string, HallSeatSmartSideLabels>;
} {
    const seatLabelsById: Record<string, string> = {};
    const sideRowLabelsBySeatId: Record<string, HallSeatSmartSideLabels> = {};

    seats.forEach((seat) => {
        seatLabelsById[seat._id] = formatHallSeatRowSlashNumber(
            seat.seatRow,
            seat.seatNumber,
        );
    });

    const rowGroups = new Map<string, any[]>();
    seats.forEach((seat) => {
        const row = normalizeHallSeatLabelValue(seat?.seatRow);
        if (!row) return;
        const sector = normalizeHallSeatLabelValue(seat?.sector) ?? '';
        const key = `${sector}\u0000${row}`;
        const group = rowGroups.get(key);
        if (group) group.push(seat);
        else rowGroups.set(key, [seat]);
    });

    rowGroups.forEach((groupSeats) => {
        const sortedByY = [...groupSeats].sort((a, b) => a.y - b.y);
        const lines: any[][] = [];

        sortedByY.forEach((seat) => {
            const line = lines[lines.length - 1];
            if (!line) {
                lines.push([seat]);
                return;
            }
            const meanY = line.reduce((sum, item) => sum + item.y, 0) / line.length;
            if (Math.abs(seat.y - meanY) <= rowTolerance) line.push(seat);
            else lines.push([seat]);
        });

        lines.forEach((line) => {
            if (line.length < 2) return;
            const orderedLine = [...line].sort((a, b) => a.x - b.x);
            const leftSeat = orderedLine[0];
            const rightSeat = orderedLine[orderedLine.length - 1];
            const row = normalizeHallSeatLabelValue(leftSeat?.seatRow);
            if (!row) return;

            orderedLine.forEach((seat) => {
                seatLabelsById[seat._id] = formatHallSeatNumberOnly(seat.seatNumber);
            });

            sideRowLabelsBySeatId[leftSeat._id] = {
                ...(sideRowLabelsBySeatId[leftSeat._id] ?? {}),
                left: row,
            };
            sideRowLabelsBySeatId[rightSeat._id] = {
                ...(sideRowLabelsBySeatId[rightSeat._id] ?? {}),
                right: row,
            };
        });
    });

    return { seatLabelsById, sideRowLabelsBySeatId };
}

// ——— Плоский список и сортировка ———

const HALL_TYPE_PAINT_ORDER: Record<string, number> = {
    sector: 0,
    sector_polygon: 0,
    zone: 1,
    stage: 2,
    table: 3,
    wall: 4,
    seat: 5,
};

function sortHallElementsByPaintOrder(items: any[]): any[] {
    return items
        .map((el, i) => ({ el, i }))
        .sort((a, b) => {
            const oa = HALL_TYPE_PAINT_ORDER[a.el.type] ?? 99;
            const ob = HALL_TYPE_PAINT_ORDER[b.el.type] ?? 99;
            if (oa !== ob) return oa - ob;
            return a.i - b.i;
        })
        .map(({ el }) => el);
}

function apiElementToRender(el: HallElementType): any | null {
    const _id = typeof el._id === 'string' && el._id ? el._id : null;
    if (!_id || !el.type) return null;
    const t = el.type;
    if (t === 'canvas') return null;
    const rot = el.rotation ?? 0;
    const label = el.label ?? '';
    if (t === 'seat') {
        return {
            _id,
            type: 'seat',
            x: el.x,
            y: el.y,
            width: el.width ?? HALL_SEAT_SIZE,
            height: el.height ?? HALL_SEAT_SIZE,
            rotation: rot,
            label,
            seatNumber: el.seat?.number,
            seatRow: el.seat?.row,
            sector: el.sector,
        };
    }
    const layout = HALL_BLOCK_LAYOUT_DEFAULTS[t] ?? { width: 1, height: 1, label: '' };
    return {
        _id,
        type: t,
        x: el.x,
        y: el.y,
        width: el.width ?? layout.width,
        height: el.height ?? layout.height,
        rotation: rot,
        label: t === 'stage' ? label || 'Сцена' : label,
        ...(t === 'sector_polygon' && Array.isArray(el.points) ? { points: el.points } : {}),
    };
}

// ——— Hit-test: точка внутри места (с учётом поворота) ———

function pointInSeat(px: number, py: number, seat: any): boolean {
    const w =
        typeof seat.width === 'number' && Number.isFinite(seat.width) && seat.width > 0
            ? seat.width
            : HALL_SEAT_SIZE;
    const h =
        typeof seat.height === 'number' && Number.isFinite(seat.height) && seat.height > 0
            ? seat.height
            : HALL_SEAT_SIZE;
    const rot = seat.rotation ?? 0;
    const rad = (-rot * Math.PI) / 180;
    const cos = Math.cos(rad);
    const sin = Math.sin(rad);
    const dx = px - seat.x;
    const dy = py - seat.y;
    const lx = dx * cos - dy * sin;
    const ly = dx * sin + dy * cos;
    return Math.abs(lx) <= w / 2 && Math.abs(ly) <= h / 2;
}

// ——— Компонент ———

type HallPreviewProps = {
    reverseSelect?: boolean;
    hallID: string;
    /** ID мест, отмеченных как выбранные. */
    selected?: string[];
    /** Если передан — включён режим выбора (клик/тап/лассо). */
    onSelect?: (ids: string[]) => void;
    /** Снятие выбора с уже выбранных мест. */
    onUnSelect?: (ids: string[]) => void;

    activeElements?: HallSelectorElementType[];
    // eventTickets?: EventTicketType[];
};

const LASSO_DRAG_THRESHOLD = 8;
const LASSO_POINT_MIN_DIST = 4;
const LASSO_MIN_BBOX = 4;

const HallPreview = (props: HallPreviewProps) => {
    const [elements, setElements] = useState<HallElementType[]>([]);

    const [loading, setLoading] = useState<boolean>(false);
    useEffect(() => {
        setLoading(true);
        rpc.hallElements({ hallUUID: props.hallID }).then((json) => {

            setLoading(false);
            setElements(json.result ?? []);
        });
    }, [props.hallID]);

    const canvasSize = useMemo(
        () => resolveHallCanvasSize(elements),
        [elements],
    );
    const stageW = canvasSize.width;
    const stageH = canvasSize.height;
    const panStep = Math.round(Math.min(stageW, stageH) * 0.2);

    const geometry = useMemo(() => {
        const flat = sortHallElementsByPaintOrder(
            elements.map(apiElementToRender).filter((e): e is any => e != null),
        );
        return {
            sectors: flat.filter((e) => e.type === 'sector'),
            sectorPolygons: flat.filter((e) => e.type === 'sector_polygon'),
            zones: flat.filter((e) => e.type === 'zone'),
            stages: flat.filter((e) => e.type === 'stage'),
            tables: flat.filter((e) => e.type === 'table'),
            walls: flat.filter((e) => e.type === 'wall'),
            seats: flat.filter((e) => e.type === 'seat'),
        };
    }, [elements]);

    const seatSmartLabels = useMemo(
        () => buildHallSeatSmartLabels(geometry.seats),
        [geometry.seats],
    );

    const interactive = !!props.onSelect;
    const selectedSet = useMemo(
        () => new Set(props.selected ?? []),
        [props.selected],
    );


    // ——— Зум / пан ———

    const MIN_SCALE = 1;
    const MAX_SCALE = 6;

    const [view, setView] = useState({ scale: 1, panX: 0, panY: 0 });
    const viewRef = useRef(view);
    viewRef.current = view;

    const clampView = (scale: number, panX: number, panY: number) => {
        const s = Math.min(MAX_SCALE, Math.max(MIN_SCALE, scale));
        const contentW = stageW * s;
        const contentH = stageH * s;
        const minPanX = stageW - contentW;
        const minPanY = stageH - contentH;
        const clampX = (v: number) =>
            contentW <= stageW ? 0 : Math.min(0, Math.max(minPanX, v));
        const clampY = (v: number) =>
            contentH <= stageH ? 0 : Math.min(0, Math.max(minPanY, v));
        return { scale: s, panX: clampX(panX), panY: clampY(panY) };
    };

    const zoomAroundViewBoxPoint = (nextScale: number, vbX: number, vbY: number) => {
        const v = viewRef.current;
        const contentX = (vbX - v.panX) / v.scale;
        const contentY = (vbY - v.panY) / v.scale;
        const targetScale = Math.min(MAX_SCALE, Math.max(MIN_SCALE, nextScale));
        const next = clampView(
            targetScale,
            vbX - contentX * targetScale,
            vbY - contentY * targetScale,
        );
        setView(next);
    };

    const zoomIn = () =>
        zoomAroundViewBoxPoint(
            viewRef.current.scale * 1.3,
            stageW / 2,
            stageH / 2,
        );
    const zoomOut = () =>
        zoomAroundViewBoxPoint(
            viewRef.current.scale / 1.3,
            stageW / 2,
            stageH / 2,
        );
    const resetView = () => setView({ scale: 1, panX: 0, panY: 0 });

    const panBy = (dx: number, dy: number) => {
        const v = viewRef.current;
        setView(clampView(v.scale, v.panX + dx, v.panY + dy));
    };

    const panLimits = useMemo(() => {
        const contentW = stageW * view.scale;
        const contentH = stageH * view.scale;
        const minPanX = stageW - contentW;
        const minPanY = stageH - contentH;
        return {
            minPanX,
            minPanY,
            canPanUp: view.panY < 0,
            canPanDown: view.panY > minPanY,
            canPanLeft: view.panX < 0,
            canPanRight: view.panX > minPanX,
        };
    }, [view.scale, view.panX, view.panY, stageW, stageH]);

    const showPanButtons = view.scale > MIN_SCALE + 0.001 && !loading;

    useEffect(() => {
        setView({ scale: 1, panX: 0, panY: 0 });
    }, [stageW, stageH]);

    const hallToolButtonClass =
        'pointer-events-none flex h-8 w-8 items-center justify-center rounded-md bg-white/90 text-slate-700 shadow-sm';

    const panEdgeButtonClass =
        'absolute z-10 flex items-center justify-center disabled:pointer-events-none disabled:opacity-30';

    // ——— Указатели ———

    const svgRef = useRef<SVGSVGElement | null>(null);

    // Координаты активных указателей в системе viewBox.
    const pointersRef = useRef<
        Map<number, { vbX: number; vbY: number }>
    >(new Map());

    const dragRef = useRef<{
        pointerId: number;
        startX: number;
        startY: number;
        currentX: number;
        currentY: number;
        dragged: boolean;
        points: HallPolygonPoint[];
    } | null>(null);
    const [lassoPoints, setLassoPoints] = useState<HallPolygonPoint[] | null>(null);

    const pinchRef = useRef<{
        pA: number;
        pB: number;
        startDist: number;
        startMidVbX: number;
        startMidVbY: number;
        startScale: number;
        startPanX: number;
        startPanY: number;
    } | null>(null);

    const clientToViewBox = (clientX: number, clientY: number) => {
        const svg = svgRef.current;
        if (!svg) return { x: 0, y: 0 };

        const ctm = svg.getScreenCTM();
        if (!ctm) return { x: 0, y: 0 };

        const pt = svg.createSVGPoint();
        pt.x = clientX;
        pt.y = clientY;
        const vbPt = pt.matrixTransform(ctm.inverse());
        return { x: vbPt.x, y: vbPt.y };
    };

    const viewBoxToContent = (vbX: number, vbY: number) => {
        const v = viewRef.current;
        return { x: (vbX - v.panX) / v.scale, y: (vbY - v.panY) / v.scale };
    };

    const cancelLasso = () => {
        dragRef.current = null;
        setLassoPoints(null);
    };

    const startPinch = () => {
        const ids = Array.from(pointersRef.current.keys());
        if (ids.length < 2) return;
        const [a, b] = ids;
        const pa = pointersRef.current.get(a)!;
        const pb = pointersRef.current.get(b)!;
        const dist = Math.hypot(pa.vbX - pb.vbX, pa.vbY - pb.vbY);
        if (dist <= 0) return;
        const v = viewRef.current;
        pinchRef.current = {
            pA: a,
            pB: b,
            startDist: dist,
            startMidVbX: (pa.vbX + pb.vbX) / 2,
            startMidVbY: (pa.vbY + pb.vbY) / 2,
            startScale: v.scale,
            startPanX: v.panX,
            startPanY: v.panY,
        };
    };

    const updatePinch = () => {
        const p = pinchRef.current;
        if (!p) return;
        const pa = pointersRef.current.get(p.pA);
        const pb = pointersRef.current.get(p.pB);
        if (!pa || !pb) return;
        const newDist = Math.hypot(pa.vbX - pb.vbX, pa.vbY - pb.vbY);
        if (newDist <= 0) return;
        const ratio = newDist / p.startDist;
        const targetScale = Math.min(
            MAX_SCALE,
            Math.max(MIN_SCALE, p.startScale * ratio),
        );
        const newMidX = (pa.vbX + pb.vbX) / 2;
        const newMidY = (pa.vbY + pb.vbY) / 2;
        // Точка под центром двух пальцев должна оставаться в той же позиции контента
        const contentMidX = (p.startMidVbX - p.startPanX) / p.startScale;
        const contentMidY = (p.startMidVbY - p.startPanY) / p.startScale;
        setView(
            clampView(
                targetScale,
                newMidX - contentMidX * targetScale,
                newMidY - contentMidY * targetScale,
            ),
        );
    };

    const onPointerDown = (e: React.PointerEvent<SVGSVGElement>) => {
        const vb = clientToViewBox(e.clientX, e.clientY);
        pointersRef.current.set(e.pointerId, { vbX: vb.x, vbY: vb.y });
        try {
            (e.currentTarget as SVGSVGElement).setPointerCapture(e.pointerId);
        } catch { }

        if (pointersRef.current.size === 1) {
            if (!interactive) return;
            const c = viewBoxToContent(vb.x, vb.y);
            dragRef.current = {
                pointerId: e.pointerId,
                startX: c.x,
                startY: c.y,
                currentX: c.x,
                currentY: c.y,
                dragged: false,
                points: [{ x: c.x, y: c.y }],
            };
        } else if (pointersRef.current.size === 2) {
            cancelLasso();
            startPinch();
        }
    };

    const onPointerMove = (e: React.PointerEvent<SVGSVGElement>) => {
        if (!pointersRef.current.has(e.pointerId)) return;
        const vb = clientToViewBox(e.clientX, e.clientY);
        pointersRef.current.set(e.pointerId, { vbX: vb.x, vbY: vb.y });

        if (pinchRef.current) {
            updatePinch();
            return;
        }

        const s = dragRef.current;
        if (!s || s.pointerId !== e.pointerId) return;
        const c = viewBoxToContent(vb.x, vb.y);
        s.currentX = c.x;
        s.currentY = c.y;
        const dx = c.x - s.startX;
        const dy = c.y - s.startY;
        const thresholdContent = LASSO_DRAG_THRESHOLD / viewRef.current.scale;
        if (!s.dragged && Math.hypot(dx, dy) > thresholdContent) {
            s.dragged = true;
        }
        if (s.dragged) {
            const last = s.points[s.points.length - 1];
            if (last) {
                const pdx = c.x - last.x;
                const pdy = c.y - last.y;
                if ((pdx * pdx + pdy * pdy) >= LASSO_POINT_MIN_DIST * LASSO_POINT_MIN_DIST) {
                    s.points.push({ x: c.x, y: c.y });
                }
            } else {
                s.points.push({ x: c.x, y: c.y });
            }
            setLassoPoints([...s.points]);
        }
    };

    const ticketByColorAndPrice = useMemo(() => {

        return _.groupBy(props.activeElements, (element) => `${element.color}-${element.price}`);
    }, [props.activeElements]);


    const finishLasso = (s: NonNullable<typeof dragRef.current>) => {
        const reverseSelect = !!props.reverseSelect;
        const isSeatSelectable = (seatId: string) => {

            const hasTicket = props.activeElements?.find((element) => element.hallElement === seatId);
            return reverseSelect ? hasTicket : !hasTicket;
        };
        const hit: string[] = [];
        if (s.dragged) {
            const path = s.points;
            const bbox = hallPolygonBBox(path);
            const tooSmall = !bbox
                || path.length < 3
                || bbox.width < LASSO_MIN_BBOX
                || bbox.height < LASSO_MIN_BBOX;
            if (tooSmall) {
                return;
            }
            for (const seat of geometry.seats) {
                if (!isSeatSelectable(seat._id)) continue;
                if (pointInHallPolygon(seat.x, seat.y, path)) {
                    hit.push(seat._id);
                }
            }
        } else {
            for (const seat of geometry.seats) {
                if (!isSeatSelectable(seat._id)) continue;
                if (pointInSeat(s.startX, s.startY, seat)) {
                    hit.push(seat._id);
                    break;
                }
            }
        }
        if (!hit.length) return;

        const toSelect: string[] = [];
        const toUnselect: string[] = [];
        for (const id of hit) {
            if (selectedSet.has(id)) toUnselect.push(id);
            else toSelect.push(id);
        }
        if (toSelect.length) props.onSelect?.(toSelect);
        if (toUnselect.length) props.onUnSelect?.(toUnselect);
    };

    const onPointerUp = (e: React.PointerEvent<SVGSVGElement>) => {
        if (!pointersRef.current.has(e.pointerId)) return;
        pointersRef.current.delete(e.pointerId);
        try {
            (e.currentTarget as SVGSVGElement).releasePointerCapture(e.pointerId);
        } catch { }

        if (pinchRef.current) {
            pinchRef.current = null;
            // Если один указатель остался — НЕ запускаем лассо (чтобы не выбрать случайно)
            return;
        }

        const s = dragRef.current;
        if (s && s.pointerId === e.pointerId) {
            dragRef.current = null;
            setLassoPoints(null);
            finishLasso(s);
        }
    };

    const onPointerCancel = (e: React.PointerEvent<SVGSVGElement>) => {
        pointersRef.current.delete(e.pointerId);
        pinchRef.current = null;
        if (dragRef.current && dragRef.current.pointerId === e.pointerId) {
            cancelLasso();
        }
    };

    // React по умолчанию вешает wheel пассивно → preventDefault() не сработает.
    // Вешаем нативный non-passive listener вручную.
    useEffect(() => {
        const svg = svgRef.current;
        if (!svg) return;
        const handler = (e: WheelEvent) => {
            e.preventDefault();
            const vb = clientToViewBox(e.clientX, e.clientY);
            const factor = Math.exp(-e.deltaY * 0.0015);
            zoomAroundViewBoxPoint(viewRef.current.scale * factor, vb.x, vb.y);
        };
        svg.addEventListener('wheel', handler, { passive: false });
        return () => svg.removeEventListener('wheel', handler);
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    const viewTransform = `translate(${view.panX} ${view.panY}) scale(${view.scale})`;



    const [viewMode, setViewMode] = useState<'places' | 'statuses'>('places');

    return (
        <div className="flex h-full min-h-0 flex-1 flex-col">

            <div className="relative flex min-h-0 flex-1 items-center justify-center overflow-hidden rounded-md bg-white outline-1 outline-slate-300">
                <div
                    className="relative h-full max-h-full w-full max-w-full"
                    style={{ aspectRatio: `${stageW} / ${stageH}` }}
                >
                    <svg
                        ref={svgRef}
                        viewBox={`0 0 ${stageW} ${stageH}`}
                        xmlns="http://www.w3.org/2000/svg"
                        onPointerDown={onPointerDown}
                        onPointerMove={onPointerMove}
                        onPointerUp={onPointerUp}
                        onPointerCancel={onPointerCancel}
                        className="block h-full w-full"
                        style={{
                            touchAction: 'none',
                            userSelect: 'none',
                        }}
                    >
                    <g transform={viewTransform}>
                        {/* Сектора */}
                        {geometry.sectors.map((item: any) => {
                            const rot = item.rotation ?? 0;
                            const transform = rot ? `rotate(${rot} ${item.x} ${item.y})` : undefined;
                            const sectorLabel = hallSectorLabelLayout({
                                x: item.x,
                                y: item.y,
                                width: item.width,
                                height: item.height,
                                rotation: rot,
                                fontSize: 14,
                            });
                            return (
                                <g key={item._id}>
                                    <g transform={transform}>
                                        <rect
                                            x={item.x}
                                            y={item.y}
                                            width={item.width}
                                            height={item.height}
                                            rx={getHallBlockCornerRadius('sector')}
                                            ry={getHallBlockCornerRadius('sector')}
                                            fill={HALL_SECTOR.fill}
                                            stroke={HALL_SECTOR.stroke}
                                            strokeWidth={HALL_SECTOR.strokeWidth}
                                        />
                                    </g>
                                    {item.label ? (
                                        <text
                                            x={sectorLabel.x}
                                            y={sectorLabel.y}
                                            transform={`rotate(${sectorLabel.rotation} ${sectorLabel.x} ${sectorLabel.y})`}
                                            textAnchor="middle"
                                            dominantBaseline="middle"
                                            fontSize={sectorLabel.fontSize}
                                            fill={HALL_SECTOR.labelRegular}
                                        >
                                            {item.label}
                                        </text>
                                    ) : null}
                                </g>
                            );
                        })}

                        {/* Сектора-полигоны */}
                        {geometry.sectorPolygons.map((item: any) => {
                            const absPoints = hallPolygonAbsolutePoints(item);
                            const path = absPoints
                                .map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x},${p.y}`)
                                .join(' ') + (absPoints.length ? ' Z' : '');
                            const sectorLabel = hallPolygonLabelLayout({
                                absPoints,
                                fontSize: 14,
                            }) ?? hallSectorLabelLayout({
                                x: item.x,
                                y: item.y,
                                width: item.width,
                                height: item.height,
                                rotation: 0,
                                fontSize: 14,
                            });
                            return (
                                <g key={item._id}>
                                    <path
                                        d={path}
                                        fill={HALL_SECTOR.fill}
                                        stroke={HALL_SECTOR.stroke}
                                        strokeWidth={HALL_SECTOR.strokeWidth}
                                    />
                                    {item.label ? (
                                        <text
                                            x={sectorLabel.x}
                                            y={sectorLabel.y}
                                            transform={`rotate(${sectorLabel.rotation} ${sectorLabel.x} ${sectorLabel.y})`}
                                            textAnchor="middle"
                                            dominantBaseline="middle"
                                            fontSize={sectorLabel.fontSize}
                                            fill={HALL_SECTOR.labelRegular}
                                        >
                                            {item.label}
                                        </text>
                                    ) : null}
                                </g>
                            );
                        })}

                        {/* Зоны */}
                        {geometry.zones.map((item: any) => {
                            const rot = item.rotation ?? 0;
                            const transform = rot ? `rotate(${rot} ${item.x} ${item.y})` : undefined;
                            const sectorLabel = hallSectorLabelLayout({
                                x: item.x,
                                y: item.y,
                                width: item.width,
                                height: item.height,
                                rotation: rot,
                                fontSize: 16,
                            });
                            return (
                                <g key={item._id}>
                                    <g transform={transform}>
                                        <rect
                                            x={item.x}
                                            y={item.y}
                                            width={item.width}
                                            height={item.height}
                                            rx={getHallBlockCornerRadius('zone')}
                                            ry={getHallBlockCornerRadius('zone')}
                                            fill={HALL_SECTOR.fill}
                                            stroke={HALL_SECTOR.stroke}
                                            strokeWidth={HALL_SECTOR.strokeWidth}
                                        />
                                    </g>
                                    {item.label ? (
                                        <text
                                            x={sectorLabel.x}
                                            y={sectorLabel.y}
                                            transform={`rotate(${sectorLabel.rotation} ${sectorLabel.x} ${sectorLabel.y})`}
                                            textAnchor="middle"
                                            dominantBaseline="middle"
                                            fontSize={sectorLabel.fontSize}
                                            fill={HALL_SECTOR.labelNoSeats}
                                        >
                                            {item.label}
                                        </text>
                                    ) : null}
                                </g>
                            );
                        })}

                        {/* Сцены */}
                        {geometry.stages.map((item: any) => {
                            const rot = item.rotation ?? 0;
                            const transform = rot ? `rotate(${rot} ${item.x} ${item.y})` : undefined;
                            return (
                                <g key={item._id} transform={transform}>
                                    <rect
                                        x={item.x}
                                        y={item.y}
                                        width={item.width}
                                        height={item.height}
                                        rx={getHallBlockCornerRadius('stage')}
                                        ry={getHallBlockCornerRadius('stage')}
                                        fill={HALL_ELEMENT_DEFAULTS.stage.fill}
                                    />
                                    <text
                                        x={item.x + item.width / 2}
                                        y={item.y + item.height / 2}
                                        textAnchor="middle"
                                        dominantBaseline="central"
                                        fontSize={16}
                                        fill={getHallBlockOnShapeTextFill('stage')}
                                    >
                                        {item.label ?? 'Сцена'}
                                    </text>
                                </g>
                            );
                        })}

                        {/* Столы */}
                        {geometry.tables.map((item: any) => {
                            const rot = item.rotation ?? 0;
                            const transform = rot ? `rotate(${rot} ${item.x} ${item.y})` : undefined;
                            return (
                                <rect
                                    key={item._id}
                                    transform={transform}
                                    x={item.x}
                                    y={item.y}
                                    width={item.width}
                                    height={item.height}
                                    rx={getHallBlockCornerRadius('table')}
                                    ry={getHallBlockCornerRadius('table')}
                                    fill={HALL_ELEMENT_DEFAULTS.table.fill}
                                />
                            );
                        })}

                        {/* Стены */}
                        {geometry.walls.map((item: any) => {
                            const rot = item.rotation ?? 0;
                            const transform = rot ? `rotate(${rot} ${item.x} ${item.y})` : undefined;
                            return (
                                <rect
                                    key={item._id}
                                    transform={transform}
                                    x={item.x}
                                    y={item.y}
                                    width={item.width}
                                    height={item.height}
                                    rx={getHallBlockCornerRadius('wall')}
                                    ry={getHallBlockCornerRadius('wall')}
                                    fill={HALL_ELEMENT_DEFAULTS.wall.fill}
                                />
                            );
                        })}

                        {/* Места */}
                        {geometry.seats.map((item: any) => {
                            const seatLabel = seatSmartLabels.seatLabelsById[item._id] ?? '';
                            const w =
                                typeof item.width === 'number' &&
                                    Number.isFinite(item.width) &&
                                    item.width > 0
                                    ? item.width
                                    : HALL_SEAT_SIZE;
                            const h =
                                typeof item.height === 'number' &&
                                    Number.isFinite(item.height) &&
                                    item.height > 0
                                    ? item.height
                                    : HALL_SEAT_SIZE;
                            const halfW = w / 2;
                            const halfH = h / 2;
                            const rot = item.rotation ?? 0;
                            const transform = rot ? `rotate(${rot} ${item.x} ${item.y})` : undefined;
                            const sideLabels = seatSmartLabels.sideRowLabelsBySeatId[item._id];
                            const rowLabelFontSize = hallSeatRowLabelFontSizeForRect(w, h);
                            const rowLabelGap = Math.max(6, Math.round(w * 0.18));

                            const ticketColor = props.activeElements?.find((element) => element.hallElement === item._id)?.color;
                            const ticketStatus = undefined;
                            let seatFill = resolveHallElementFill(item);

                            if (viewMode === 'places') {
                                seatFill = ticketColor ?? resolveHallElementFill(item);
                            } else {
                                //Цвет статуса
                                seatFill = resolveHallElementFill(item);// ticketStatus
                                // ? statusColors[ticketStatus]
                                // : resolveHallElementFill(item);
                            }

                            const seatLabelFill = ticketColor
                                ? hallContrastTextOnFill(ticketColor)
                                : hallSeatApiLabelFill();
                            const seatFontSize = hallSeatLabelFontSizeForRect(seatLabel, w, h);

                            const isSelected = selectedSet.has(item._id);

                            return (
                                <g key={item._id} transform={transform}>
                                    <rect
                                        x={item.x - halfW}
                                        y={item.y - halfH}
                                        width={w}
                                        height={h}
                                        rx={getHallSeatCornerRadius(w, h)}
                                        ry={getHallSeatCornerRadius(w, h)}
                                        fill={seatFill}
                                        stroke={isSelected ? '#000000' : undefined}
                                        strokeWidth={isSelected ? 2 : 0}
                                    />
                                    {seatLabel ? (
                                        <text
                                            x={item.x}
                                            y={item.y}
                                            textAnchor="middle"
                                            dominantBaseline="central"
                                            fontSize={seatFontSize}
                                            fontWeight="bold"
                                            fontFamily="Arial,sans-serif"
                                            fill={seatLabelFill}
                                        >
                                            {seatLabel}
                                        </text>
                                    ) : null}
                                    {sideLabels?.left ? (
                                        <text
                                            x={item.x - halfW - rowLabelGap}
                                            y={item.y}
                                            textAnchor="end"
                                            dominantBaseline="central"
                                            fontSize={rowLabelFontSize}
                                            fontWeight="bold"
                                            fontFamily="Arial,sans-serif"
                                            fill={hallSeatRowLabelFill()}
                                        >
                                            {sideLabels.left}
                                        </text>
                                    ) : null}
                                    {sideLabels?.right ? (
                                        <text
                                            x={item.x + halfW + rowLabelGap}
                                            y={item.y}
                                            textAnchor="start"
                                            dominantBaseline="central"
                                            fontSize={rowLabelFontSize}
                                            fontWeight="bold"
                                            fontFamily="Arial,sans-serif"
                                            fill={hallSeatRowLabelFill()}
                                        >
                                            {sideLabels.right}
                                        </text>
                                    ) : null}
                                </g>
                            );
                        })}

                        {/* LASSO-контур (поверх всего) */}
                        {lassoPoints && lassoPoints.length >= 2 ? (
                            lassoPoints.length >= 3 ? (
                                <polygon
                                    points={lassoPoints.map((p) => `${p.x},${p.y}`).join(' ')}
                                    fill="rgba(0,0,0,0.05)"
                                    stroke="#000000"
                                    strokeWidth={1 / view.scale}
                                    strokeDasharray={`${4 / view.scale} ${3 / view.scale}`}
                                    strokeLinejoin="round"
                                    strokeLinecap="round"
                                    pointerEvents="none"
                                />
                            ) : (
                                <polyline
                                    points={lassoPoints.map((p) => `${p.x},${p.y}`).join(' ')}
                                    fill="none"
                                    stroke="#000000"
                                    strokeWidth={1 / view.scale}
                                    strokeDasharray={`${4 / view.scale} ${3 / view.scale}`}
                                    strokeLinejoin="round"
                                    strokeLinecap="round"
                                    pointerEvents="none"
                                />
                            )
                        ) : null}
                    </g>
                </svg>
                </div>

                {showPanButtons ? (
                    <>
                        <button
                            type="button"
                            className={`${panEdgeButtonClass} top-0 right-0 left-0 h-8`}
                            onClick={() => panBy(0, panStep)}
                            disabled={!panLimits.canPanUp}
                            aria-label="Сдвинуть вверх"
                        >
                            <span className={hallToolButtonClass}>
                                <ChevronUp className="size-4" />
                            </span>
                        </button>
                        <button
                            type="button"
                            className={`${panEdgeButtonClass} right-0 bottom-0 left-0 h-8`}
                            onClick={() => panBy(0, -panStep)}
                            disabled={!panLimits.canPanDown}
                            aria-label="Сдвинуть вниз"
                        >
                            <span className={hallToolButtonClass}>
                                <ChevronDown className="size-4" />
                            </span>
                        </button>
                        <button
                            type="button"
                            className={`${panEdgeButtonClass} top-0 bottom-0 left-0 w-8`}
                            onClick={() => panBy(panStep, 0)}
                            disabled={!panLimits.canPanLeft}
                            aria-label="Сдвинуть влево"
                        >
                            <span className={hallToolButtonClass}>
                                <ChevronLeft className="size-4" />
                            </span>
                        </button>
                        <button
                            type="button"
                            className={`${panEdgeButtonClass} top-0 right-0 bottom-0 w-8`}
                            onClick={() => panBy(-panStep, 0)}
                            disabled={!panLimits.canPanRight}
                            aria-label="Сдвинуть вправо"
                        >
                            <span className={hallToolButtonClass}>
                                <ChevronRight className="size-4" />
                            </span>
                        </button>
                    </>
                ) : null}

                <div className="absolute right-2 bottom-2 z-20 flex flex-col gap-1 rounded-md bg-white/90 shadow-sm">
                    <button
                        type="button"
                        onClick={zoomIn}
                        disabled={view.scale >= MAX_SCALE - 0.001}
                        className="w-8 h-8 flex items-center justify-center text-xl leading-none text-slate-700 disabled:opacity-30"
                        aria-label="Увеличить"
                    >
                        +
                    </button>
                    <button
                        type="button"
                        onClick={zoomOut}
                        disabled={view.scale <= MIN_SCALE + 0.001}
                        className="w-8 h-8 flex items-center justify-center text-xl leading-none text-slate-700 disabled:opacity-30"
                        aria-label="Уменьшить"
                    >
                        −
                    </button>
                    <button
                        type="button"
                        onClick={resetView}
                        disabled={view.scale === 1 && view.panX === 0 && view.panY === 0}
                        className="w-8 h-8 flex items-center justify-center text-xs text-slate-700 disabled:opacity-30"
                        aria-label="Сбросить"
                    >
                        1:1
                    </button>
                </div>

                {loading && <div><Skeleton
                    className='absolute top-0 left-0 w-full h-full'
                /></div>}
            </div>

            {!!_.size(ticketByColorAndPrice) && <div className='mt-1 flex shrink-0 flex-wrap gap-1'>
                {_.map(ticketByColorAndPrice, ((val) => {
                    return <Badge
                        style={{
                            backgroundColor: val[0].color,
                            color: '#fff',
                        }}
                    >{val[0].price} ₽ (мест: {val.length})</Badge>
                }))}
            </div>}



        </div>
    );
};

export default HallPreview;
