import type {
    HallBeltType,
    HallSeatType,
} from '@/api/types';

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

/** Логический размер сцены 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;
}

function rgbToHex(r: number, g: number, b: number): string {
    const c = (x: number) => Math.max(0, Math.min(255, Math.round(x))).toString(16).padStart(2, '0');
    return `#${c(r)}${c(g)}${c(b)}`;
}

/** 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';
}

/**
 * Затемняет цвет: на каждый «шаг» каналы умножаются на (1 − p), p ∈ [0, 1].
 * Несколько шагов: итог как умножение на (1 − p)^steps (эквивалентно шагам по кругу).
 */
export function hallBeltDarkenHex(baseHex: string, darkenFraction: number, steps: number): string {
    const rgb = parseHexRgb(baseHex);
    if (!rgb) {
        return baseHex;
    }
    const p = Math.min(1, Math.max(0, darkenFraction));
    const mult = steps <= 0 ? 1 : (1 - p) ** steps;
    return rgbToHex(rgb.r * mult, rgb.g * mult, rgb.b * mult);
}

/**
 * Редактор: создание поясов и импорт старых данных.
 * На схеме цвет места всегда из `belt.color` в данных зала.
 */
export const HALL_EDITOR_BELT = {
    /** Если в JSON у пояса нет `color`. */
    legacyFallbackColor: '#f59e0b',
    /**
     * Базовые цвета новых поясов (по кругу: 0, 1, …, N−1, снова 0, …).
     * Меняете только базовые — более тёмные варианты считаются сами.
     */
    baseColors: [
        '#CD2E1F',
        '#ED9D34',
        '#71CD63',
        '#8DD9EF',
        '#91359B',
    ],
    /**
     * После каждого полного круга (каждые `baseColors.length` поясов) тот же базовый оттенок
     * затемняется на эту долю (0…100), накопительно: круг 0 — как в `baseColors`, круг 1 — один раз темнее, и т.д.
     * Формула: RGB × (1 − p/100)^круг.
     */
    darkenPercentPerRound: 15,
} satisfies {
    legacyFallbackColor: string;
    baseColors: readonly string[];
    darkenPercentPerRound: number;
};

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

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

/** Как рисуется прямоугольник сектора на схеме (не «призрак» с палитры). */
export type HallSectorSchemeConfig = {
    rectFill: string;
    stroke: string;
    strokeWidth: number;
    /** Подпись названия сектора на схеме */
    nameText: string;
    /** Строка «Вместимость: …» только у `sector_no_seats` */
    capacityText?: 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: 'Сектор' },
    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',
        },
    },
    zone: {
        fill: '#9E9E9E',
        cornerRadius: 6,
        textOnShape: '#90A4AE',
        scheme: {
            rectFill: '#ffffff',
            stroke: '#EEEEEE',
            strokeWidth: 2,
            nameText: '#475569',
            capacityText: '#64748b',
        },
    },
};

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

export const HALL_LEGACY_BELT_COLOR = HALL_EDITOR_BELT.legacyFallbackColor;

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,
    capacityText: HALL_BLOCK_CONFIG.zone.scheme!.capacityText!,
};

/** Подпись места на схеме: «ряд/место»; пустое поле — «*». */
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}`;
}

/** Совпадает с дефолтом 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 getAutoBeltColorForIndex(index: number): string {
    const { baseColors, darkenPercentPerRound } = HALL_EDITOR_BELT;
    const n = baseColors.length;
    if (n === 0) {
        return HALL_EDITOR_BELT.legacyFallbackColor;
    }
    const hue = index % n;
    const round = Math.floor(index / n);
    const base = baseColors[hue];
    const p = Math.min(100, Math.max(0, darkenPercentPerRound)) / 100;
    return hallBeltDarkenHex(base, p, round);
}

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

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,
    belts: HallBeltType[] | undefined,
): 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 resolveHallSeatApiFill(element, belts);
    }
    return HALL_ELEMENT_DEFAULTS[t].fill;
}

/** Заливка места из полей `HallType.seats` (без сборки `any`). */
export function resolveHallSeatApiFill(
    seat: Pick<HallSeatType, 'beltId'>,
    belts: HallBeltType[] | undefined,
): string {
    const neutral = HALL_BLOCK_CONFIG.seat.neutralFill!;
    const belt = seat.beltId && belts?.length
        ? belts.find((b) => b.id === seat.beltId)
        : undefined;
    return belt?.color ?? neutral;
}

/** Цвет подписи места над заливкой пояса / нейтральной заливке. */
export function hallSeatLabelFill(element: any, belts: HallBeltType[] | undefined): string {
    return hallContrastTextOnFill(resolveHallElementFill(element, belts));
}

export function hallSeatApiLabelFill(seat: Pick<HallSeatType, 'beltId'>, belts: HallBeltType[] | undefined): string {
    return hallContrastTextOnFill(resolveHallSeatApiFill(seat, belts));
}
