import type { HallElementType } from '@/api/types';

import {
    hallPolygonDefaultRelativePoints,
    hallPolygonFlatToPoints,
    isHallSeatSectorType,
    seatCenterInHallSector,
} from './hallPolygon';
import {
    HALL_SEAT_SIZE,
    HALL_STAGE_SIZE,
} from './hallTheme';

const HALL_NEW_ID_ALPHANUM = 'abcdefghijklmnopqrstuvwxyz0123456789';

/** Минимальный размер полотна (логич. px). */
export const HALL_CANVAS_SIZE_MIN = 100;

/** Порядок отрисовки (ниже — «под» верхними слоями). `canvas` — мета, не рисуется. */
const HALL_TYPE_PAINT_ORDER: Record<string, number> = {
    canvas: -1,
    sector: 0,
    sector_polygon: 0,
    zone: 1,
    stage: 2,
    table: 3,
    wall: 4,
    seat: 5,
};

export type HallCanvasSize = {
    width: number;
    height: number;
};

function clampHallCanvasDim(value: unknown, fallback: number): number {
    const n = typeof value === 'number' ? value : Number(value);
    if (!Number.isFinite(n)) {
        return fallback;
    }
    return Math.max(HALL_CANVAS_SIZE_MIN, Math.round(n));
}

/** Размер полотна из элемента `canvas` или дефолт 800×800. */
export function resolveHallCanvasSize(elements: any[] | null | undefined): HallCanvasSize {
    const canvas = Array.isArray(elements)
        ? elements.find((e) => e?.type === 'canvas')
        : null;
    if (!canvas) {
        return { width: HALL_STAGE_SIZE, height: HALL_STAGE_SIZE };
    }
    return {
        width: clampHallCanvasDim(canvas.width, HALL_STAGE_SIZE),
        height: clampHallCanvasDim(canvas.height, HALL_STAGE_SIZE),
    };
}

export function makeHallCanvasElement(
    width = HALL_STAGE_SIZE,
    height = HALL_STAGE_SIZE,
    hallUUID?: string,
): any {
    return {
        _id: makeHallElementId(),
        type: 'canvas',
        x: 0,
        y: 0,
        width: clampHallCanvasDim(width, HALL_STAGE_SIZE),
        height: clampHallCanvasDim(height, HALL_STAGE_SIZE),
        rotation: 0,
        label: '',
        ...(hallUUID ? { hallUUID } : {}),
    };
}

/**
 * Ровно один `canvas` в списке: создаёт при отсутствии, оставляет первый,
 * нормализует width/height, убирает дубликаты.
 */
export function ensureHallCanvas(elements: any[]): any[] {
    const list = Array.isArray(elements) ? [...elements] : [];
    const canvases = list.filter((e) => e?.type === 'canvas');
    const rest = list.filter((e) => e?.type !== 'canvas');
    if (!canvases.length) {
        const hallUUID = rest.find((e) => typeof e?.hallUUID === 'string' && e.hallUUID.trim())
            ?.hallUUID;
        return sortHallElementsByPaintOrder([
            makeHallCanvasElement(HALL_STAGE_SIZE, HALL_STAGE_SIZE, hallUUID),
            ...rest,
        ]);
    }
    const first = canvases[0]!;
    const size = resolveHallCanvasSize([first]);
    const normalized = {
        ...first,
        type: 'canvas',
        x: 0,
        y: 0,
        width: size.width,
        height: size.height,
        rotation: 0,
    };
    return sortHallElementsByPaintOrder([normalized, ...rest]);
}

/** Новые элементы в редакторе: `tml_` + 7 символов. */
export function makeHallElementId(): string {
    let suffix = '';
    for (let i = 0; i < 7; i += 1) {
        suffix += HALL_NEW_ID_ALPHANUM[Math.floor(Math.random() * HALL_NEW_ID_ALPHANUM.length)];
    }
    return `tml_${suffix}`;
}

/** Общий UUID зала для всех элементов `HallElementType`. */
export function makeHallUUID(): string {
    if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
        return crypto.randomUUID();
    }
    let suffix = '';
    for (let i = 0; i < 16; i += 1) {
        suffix += HALL_NEW_ID_ALPHANUM[Math.floor(Math.random() * HALL_NEW_ID_ALPHANUM.length)];
    }
    return `hll_${suffix}`;
}

export type CoerceHallUUIDResult =
    | { ok: true; elements: any[] }
    | { ok: false };

/**
 * Один непустой hallUUID на весь список: дополняет пустые, при 0 UUID — генерирует и проставляет всем.
 * Два и более различных непустых значения — конфликт данных.
 */
export function coerceHallElementsSingleUUID(elements: any[]): CoerceHallUUIDResult {
    const trimmed = elements.map((e) => {
        const u = e?.hallUUID;
        return typeof u === 'string' ? u.trim() : '';
    });
    const nonEmpty = trimmed.filter(Boolean);
    const distinct = [...new Set(nonEmpty)];
    if (distinct.length > 1) {
        return { ok: false };
    }
    const uuid = distinct.length === 1 ? distinct[0]! : makeHallUUID();
    return {
        ok: true,
        elements: elements.map((e) => ({ ...e, hallUUID: uuid })),
    };
}

/** Стабильная сортировка по слою отрисовки. */
export 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);
}

/** Обновить у мест `sector` по вхождению в сектора (прямоугольник / полигон). */
export const reconcileSeatSectorsLayout = (elements: any[]): any[] => {
    const sectors = elements.filter((e) => isHallSeatSectorType(e.type));
    if (!sectors.length) {
        return elements.map((e) => (e.type === 'seat' ? { ...e, sector: undefined } : e));
    }
    const sectorsByPriority = [...sectors];
    return elements.map((e) => {
        if (e.type !== 'seat') {
            return e;
        }
        let hit: any | null = null;
        for (let i = sectorsByPriority.length - 1; i >= 0; i--) {
            const s = sectorsByPriority[i];
            if (seatCenterInHallSector(e, s)) {
                hit = s;
                break;
            }
        }
        if (!hit) {
            if (e.sector == null || e.sector === '') {
                return e;
            }
            return { ...e, sector: undefined };
        }
        if (e.sector === hit._id) {
            return e;
        }
        return { ...e, sector: hit._id };
    });
};

/** Массив `HallElementType` из API → элемент редактора. */
export function hallElementApiToEditor(el: HallElementType): any | null {
    const ext = el as HallElementType & { seatNumber?: string; seatRow?: string };
    const _id = typeof el._id === 'string' && el._id ? el._id : null;
    if (!_id || !el.type) {
        return null;
    }
    const t = el.type;
    const label = el.label ?? '';
    const rot = el.rotation ?? 0;
    const hallUUID = typeof ext.hallUUID === 'string' && ext.hallUUID.trim()
        ? ext.hallUUID.trim()
        : undefined;

    if (t === 'seat') {
        const sn = el.seat?.number ?? ext.seatNumber;
        const sr = el.seat?.row ?? ext.seatRow;
        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: sn,
            seatRow: sr,
            sector: el.sector,
            ...(hallUUID ? { hallUUID } : {}),
        };
    }
    if (t === 'table') {
        return {
            _id,
            type: 'table',
            x: el.x,
            y: el.y,
            width: el.width ?? 64,
            height: el.height ?? 64,
            rotation: rot,
            ...(hallUUID ? { hallUUID } : {}),
        };
    }
    if (t === 'stage') {
        return {
            _id,
            type: 'stage',
            x: el.x,
            y: el.y,
            width: el.width ?? 240,
            height: el.height ?? 110,
            rotation: rot,
            label: label || 'Сцена',
            ...(hallUUID ? { hallUUID } : {}),
        };
    }
    if (t === 'wall') {
        return {
            _id,
            type: 'wall',
            x: el.x,
            y: el.y,
            width: el.width ?? 180,
            height: el.height ?? 18,
            rotation: rot,
            label: '',
            ...(hallUUID ? { hallUUID } : {}),
        };
    }
    if (t === 'sector') {
        return {
            _id,
            type: 'sector',
            x: el.x,
            y: el.y,
            width: el.width ?? 260,
            height: el.height ?? 160,
            rotation: rot,
            label: label || 'Новый сектор',
            ...(hallUUID ? { hallUUID } : {}),
        };
    }
    if (t === 'sector_polygon') {
        const width = el.width ?? 260;
        const height = el.height ?? 160;
        const fromApi = hallPolygonFlatToPoints(el.points);
        const points = fromApi.length >= 3
            ? el.points!
            : hallPolygonDefaultRelativePoints(width, height);
        return {
            _id,
            type: 'sector_polygon',
            x: el.x,
            y: el.y,
            width,
            height,
            rotation: rot,
            label: label || 'Новый сектор',
            points: [...points],
            ...(hallUUID ? { hallUUID } : {}),
        };
    }
    if (t === 'zone') {
        return {
            _id,
            type: 'zone',
            x: el.x,
            y: el.y,
            width: el.width ?? 260,
            height: el.height ?? 160,
            rotation: rot,
            label: label || 'Новый сектор без мест',
            ...(hallUUID ? { hallUUID } : {}),
        };
    }
    if (t === 'canvas') {
        const size = resolveHallCanvasSize([{
            type: 'canvas',
            width: el.width,
            height: el.height,
        }]);
        return {
            _id,
            type: 'canvas',
            x: 0,
            y: 0,
            width: size.width,
            height: size.height,
            rotation: 0,
            label: '',
            ...(hallUUID ? { hallUUID } : {}),
        };
    }
    return null;
}

/**
 * Признак JSON для импорта зала: корневой массив `HallElementType[]`
 * (тот же формат, что отдаёт экспорт редактора).
 */
export function isHallImportJsonValue(value: unknown): boolean {
    return Array.isArray(value);
}

/** Нормализованный плоский список элементов редактора из значения поля / JSON-импорта. */
export function hallElementsFromInitValue(value: unknown): any[] {
    if (value == null) {
        return ensureHallCanvas([]);
    }
    if (!Array.isArray(value)) {
        return ensureHallCanvas([]);
    }
    const items = value
        .map((el) => (el && typeof el === 'object' && 'type' in el
            ? hallElementApiToEditor(el as HallElementType)
            : null))
        .filter((e): e is any => e != null);
    return ensureHallCanvas(sortHallElementsByPaintOrder(reconcileSeatSectorsLayout(items)));
}

/** Редактор → `HallElementType` для сохранения / экспорта. */
export function editorElementToHallElementType(el: any): HallElementType {
    const _id = typeof el._id === 'string' && el._id ? el._id : '';
    const base: HallElementType = {
        x: el.x,
        y: el.y,
        _id,
        type: el.type,
    };
    const hallU = typeof el.hallUUID === 'string' ? el.hallUUID.trim() : '';
    if (hallU) {
        base.hallUUID = hallU;
    }
    if (el.rotation != null) {
        base.rotation = el.rotation;
    }
    if (el.label != null && String(el.label).length) {
        base.label = String(el.label);
    }
    const t = el.type as string;
    if (t === 'seat') {
        base.width = el.width;
        base.height = el.height;
        const seat: { number?: string; row?: string } = {};
        if (el.seatNumber != null && String(el.seatNumber).trim()) {
            seat.number = String(el.seatNumber).trim();
        }
        if (el.seatRow != null && String(el.seatRow).trim()) {
            seat.row = String(el.seatRow).trim();
        }
        if (Object.keys(seat).length) {
            base.seat = seat;
        }
        if (el.sector != null && String(el.sector).trim()) {
            base.sector = String(el.sector).trim();
        }
        return base;
    }
    if (t === 'table' || t === 'stage' || t === 'wall' || t === 'sector') {
        base.width = el.width;
        base.height = el.height;
        return base;
    }
    if (t === 'sector_polygon') {
        base.width = el.width;
        base.height = el.height;
        const pts = hallPolygonFlatToPoints(el.points);
        if (pts.length >= 3) {
            const flat: number[] = [];
            pts.forEach((p) => {
                flat.push(p.x, p.y);
            });
            base.points = flat;
        }
        return base;
    }
    if (t === 'zone') {
        base.width = el.width;
        base.height = el.height;
        return base;
    }
    if (t === 'canvas') {
        const size = resolveHallCanvasSize([el]);
        base.x = 0;
        base.y = 0;
        base.width = size.width;
        base.height = size.height;
        base.rotation = 0;
        return base;
    }
    return base;
}

export function buildHallElementsArray(elements: any[]): HallElementType[] {
    return ensureHallCanvas(sortHallElementsByPaintOrder(elements))
        .map(editorElementToHallElementType);
}

/** Плоский список для превью. */
export function flattenHallValueToElements(value: unknown): any[] {
    const raw = hallElementsFromInitValue(value);
    const r = coerceHallElementsSingleUUID(raw);
    return r.ok ? r.elements : raw;
}
