

// ——— Редактор (не относится к виду блоков зала) ———

/** Логический размер сцены Konva (px). Редактор и превью должны совпадать. */
export const HALL_STAGE_SIZE = 800;

export const HALL_SEAT_SIZE = 28;
export const HALL_SEAT_GAP = 3;

export const HALL_ZOOM = {
    min: 0.4,
    max: 2.5,
    step: 0.1,
} as const;

/** Длительность подсветки дубликатов при проверке зала (мс). */
export const HALL_CHECK_FLASH_MS = 3000;

/** Обводка и фон контейнера стадии в редакторе. */
export const HALL_CANVAS = {
    background: '#ffffff',
    outline: '2px solid #94a3b8',
} as const;

/**
 * Фоновая сетка только в редакторе: под всеми элементами, не участвует в hit-test.
 * Шаг в логических px сцены (до zoom).
 */
export const HALL_EDITOR_GRID = {
    /** Расстояние между линиями. */
    step: 16,
    stroke: '#e2e8f0',
    strokeWidth: 1,
} as const;

/** Рамка при рисовании сетки мест. */
export const HALL_SEAT_DRAFT = {
    rectFill: 'rgba(59,130,246,0.12)',
    rectStroke: '#3b82f6',
    previewFill: 'rgba(59,130,246,0.25)',
    previewStroke: 'rgba(59,130,246,0.7)',
    statsBg: 'rgba(15,23,42,0.82)',
    statsText: '#f8fafc',
} as const;

/** Прямоугольное выделение и рамка группы. */
export const HALL_SELECTION = {
    rectFill: 'rgba(14,165,233,0.10)',
    rectStroke: '#0ea5e9',
    frameStroke: '#0ea5e9',
    frameFill: 'rgba(14,165,233,0.04)',
} as const;

export const HALL_DUPLICATE_HIGHLIGHT_STROKE = '#ef4444';

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;
}

/** WCAG: относительная яркость sRGB, 0…1 */
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;
    };
    const r = lin(rgb.r);
    const g = lin(rgb.g);
    const b = lin(rgb.b);
    return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}

/**
 * Белый или чёрный текст на заданном фоне — тот вариант, у которого выше контраст (WCAG L1/L2).
 */
export 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';
}

// ——— Конфиг вида блоков: один объект «тип → настройки» ———

/**
 * В Konva `Rect`: радиус ≥ половины меньшей стороны → круг / «таблетка».
 * Используйте в `cornerRadius` типов, которым нужна круглая форма.
 */
export const HALL_KONVA_FULL_ROUND_RECT_RADIUS = 6;

/** Как рисуется прямоугольник сектора на схеме (не «призрак» с палитры). */
export type HallSectorSchemeConfig = {
    rectFill: string;
    stroke: string;
    strokeWidth: number;
    /** Подпись названия сектора на схеме */
    nameText: string;
};

export type HallBlockVisualConfig = {
    /**
     * Заливка блока на схеме и при создании с палитры.
     * У `seat` — `neutralFill` (серый).
     */
    fill?: string;
    cornerRadius: number;
    /** Текст на заливке фигуры (номер места, подпись стола / сцены / стены). */
    textOnShape: string;
    /** Только `seat`: заливка без пояса. */
    neutralFill?: string;
    /** Только `seat`: цифра ряда слева от места. */
    seatRowText?: string;
    /** Только `sector` / `sector_no_seats`. */
    scheme?: HallSectorSchemeConfig;
};

/**
 * Размер и подпись нового элемента по умолчанию (палитра редактора).
 * Не участвует в конфигурации отрисовки на схеме — только в раскладке при создании.
 */
export 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: 'Зона (входной билет, без места)' },
};

/**
 * Все настройки отрисовки по типу блока (схема зала).
 * Менять внешний вид — править здесь, по ключу типа.
 */
export 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',
        },
    },
};

// ——— Срезы и хелперы (читают `HALL_BLOCK_CONFIG`) ———

export const HALL_SEAT_NEUTRAL_FILL = HALL_BLOCK_CONFIG.seat.neutralFill!;

/** Рамка сектора на схеме + цвета подписей (как ожидают редактор / превью). */
export 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,
};

/** Подпись места на схеме: «ряд/место»; пустое поле — «*». */
export 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 normalizeHallSeatLabelValue(value: string | null | undefined): string | null {
    if (value == null) {
        return null;
    }
    const normalized = String(value).trim();
    return normalized !== '' ? normalized : null;
}

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

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

export 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 };
}

/** Совпадает с дефолтом Konva.Text (canvas), чтобы measureText совпадал с отрисовкой. */
const HALL_SEAT_LABEL_MEASURE_FONT = 'Arial,sans-serif';

let hallSeatLabelMeasureCanvas: HTMLCanvasElement | null = null;

function measureHallSeatLabelWidthPx(text: string, fontSize: number): number {
    if (typeof document === 'undefined') {
        return text.length * fontSize * 0.58;
    }
    if (!hallSeatLabelMeasureCanvas) {
        hallSeatLabelMeasureCanvas = document.createElement('canvas');
    }
    const ctx = hallSeatLabelMeasureCanvas.getContext('2d');
    if (!ctx) {
        return text.length * fontSize * 0.58;
    }
    ctx.font = `bold ${fontSize}px ${HALL_SEAT_LABEL_MEASURE_FONT}`;
    return ctx.measureText(text).width;
}

/**
 * Размер шрифта подписи места (одна строка, без переносов), чтобы текст помещался в фигуру.
 * У мест `cornerRadius` «полный» — визуально круг/капсула; отступы оставляют запас от края.
 */
export 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 lineH = 1.08;
    const maxW = Math.max(4, w * padW);
    const maxLinePx = Math.max(4, h * padH);
    const maxFont = Math.min(32, Math.floor(Math.min(w, h) * 0.92));
    const minFont = 5;
    let low = minFont;
    let high = maxFont;
    let best = minFont;
    while (low <= high) {
        const mid = Math.floor((low + high) / 2);
        const tw = measureHallSeatLabelWidthPx(text, mid);
        const th = mid * lineH;
        if (tw <= maxW && th <= maxLinePx) {
            best = mid;
            low = mid + 1;
        } else {
            high = mid - 1;
        }
    }
    return best;
}

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

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

/** Полное скругление (100%): в Konva радиус ≥ половины меньшей стороны → круг / капсула. */
export 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;
}

export type HallElementPaletteDefaults = {
    type: string;
    width: number;
    height: number;
    fill: string;
    label: string;
};

export const HALL_ELEMENT_DEFAULTS: Record<string, HallElementPaletteDefaults> = (() => {
    const types = Object.keys(HALL_BLOCK_CONFIG) as string[];
    const out = {} as Record<string, HallElementPaletteDefaults>;
    for (const t of types) {
        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;
})();

export 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;
}

/** Цвет подписи места над заливкой. */
export function hallSeatLabelFill(element: any): string {
    return hallContrastTextOnFill(resolveHallElementFill(element));
}

export function hallSeatApiLabelFill(): string {
    return hallContrastTextOnFill(HALL_BLOCK_CONFIG.seat.neutralFill!);
}

export function hallSeatRowLabelFill(): string {
    return HALL_BLOCK_CONFIG.seat.seatRowText ?? '#000000';
}

export 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)));
}
