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

import { HALL_SEAT_SIZE } from './hallTheme';

const HALL_NEW_ID_ALPHANUM = 'abcdefghijklmnopqrstuvwxyz0123456789';

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

/** Новые элементы в редакторе: `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);
}

export const hallSplitCountLegacy = (h: Record<string, unknown> | null | undefined): number => {
    if (!h) {
        return 0;
    }
    return (
        ((h.seats as unknown[] | undefined)?.length ?? 0)
        + ((h.tables as unknown[] | undefined)?.length ?? 0)
        + ((h.stages as unknown[] | undefined)?.length ?? 0)
        + ((h.walls as unknown[] | undefined)?.length ?? 0)
        + ((h.zones as unknown[] | undefined)?.length ?? 0)
        + ((h.sectors as unknown[] | undefined)?.length ?? 0)
    );
};

const legacySeatToEditor = (s: any): any => ({
    _id: typeof s._id === 'string' && s._id ? s._id : String(s.id ?? ''),
    type: 'seat',
    x: s.x,
    y: s.y,
    width: HALL_SEAT_SIZE,
    height: HALL_SEAT_SIZE,
    rotation: s.rotation ?? 0,
    label: '',
    seatNumber: s.seatNumber,
    seatRow: s.seatRow,
    sector: (typeof s.sector === 'string' && s.sector.trim())
        ? s.sector.trim()
        : (s.sectorId != null && String(s.sectorId).trim() ? String(s.sectorId).trim() : undefined),
});

const legacyTableToEditor = (t: any): any => ({
    _id: typeof t._id === 'string' && t._id ? t._id : String(t.id ?? ''),
    type: 'table',
    x: t.x,
    y: t.y,
    width: t.width,
    height: t.height,
    rotation: t.rotation ?? 0,
});

const legacyStageToEditor = (s: any): any => ({
    _id: typeof s._id === 'string' && s._id ? s._id : String(s.id ?? ''),
    type: 'stage',
    x: s.x,
    y: s.y,
    width: s.width,
    height: s.height,
    rotation: s.rotation ?? 0,
    label: s.label ?? 'Сцена',
});

const legacyWallToEditor = (w: any): any => ({
    _id: typeof w._id === 'string' && w._id ? w._id : String(w.id ?? ''),
    type: 'wall',
    x: w.x,
    y: w.y,
    width: w.width,
    height: w.height,
    rotation: w.rotation ?? 0,
    label: '',
});

const legacySectorToEditor = (s: any): any => ({
    _id: typeof s._id === 'string' && s._id ? s._id : String(s.id ?? ''),
    type: 'sector',
    x: s.x,
    y: s.y,
    width: s.width,
    height: s.height,
    rotation: s.rotation ?? 0,
    label: s.label ?? '',
});

const legacyZoneToEditor = (z: any): any => ({
    _id: typeof z._id === 'string' && z._id ? z._id : String(z.id ?? ''),
    type: 'zone',
    x: z.x,
    y: z.y,
    width: z.width,
    height: z.height,
    rotation: z.rotation ?? 0,
    label: z.label ?? '',
});

/** Старый объект зала с разнесёнными массивами → один массив (порядок как при merge). */
export const apiHallToEditorElements = (h: Record<string, any>): any[] => [
    ...(h.sectors ?? []).map(legacySectorToEditor),
    ...(h.zones ?? []).map(legacyZoneToEditor),
    ...(h.stages ?? []).map(legacyStageToEditor),
    ...(h.tables ?? []).map(legacyTableToEditor),
    ...(h.walls ?? []).map(legacyWallToEditor),
    ...(h.seats ?? []).map(legacySeatToEditor),
];

/** Обновить у мест `sector` по вхождению в прямоугольники секторов. */
export const reconcileSeatSectorsLayout = (elements: any[]): any[] => {
    const sectors = elements.filter((e) => e.type === 'sector');
    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;
        }
        const cx = e.x;
        const cy = e.y;
        let hit: any | null = null;
        for (let i = sectorsByPriority.length - 1; i >= 0; i--) {
            const s = sectorsByPriority[i];
            const inside = cx >= s.x && cx <= s.x + s.width && cy >= s.y && cy <= s.y + s.height;
            if (inside) {
                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 };
    });
};

const numField = (v: unknown, fallback: number): number => {
    const n = typeof v === 'number' ? v : Number(v);
    return Number.isFinite(n) ? n : fallback;
};

/** Элемент из JSON; устаревшие поля (`sectorId` и т.д.) подхватываются при разборе. */
export const normalizeHallElementFromJson = (raw: unknown): any | null => {
    if (!raw || typeof raw !== 'object') {
        return null;
    }
    const o = raw as Record<string, unknown>;
    const rid = typeof o.id === 'string' ? o.id : (typeof o._id === 'string' ? o._id : null);
    if (rid == null) {
        return null;
    }
    const typeRaw = o.type;
    if (typeof typeRaw !== 'string') {
        return null;
    }
    const type: string = typeRaw === 'stage_round' ? 'stage' : typeRaw;

    const isSeat = type === 'seat';
    const hallUuidRaw = typeof o.hallUUID === 'string' ? o.hallUUID.trim() : '';
    const el: any = {
        _id: rid,
        type,
        x: numField(o.x, 0),
        y: numField(o.y, 0),
        width: isSeat ? numField(o.width, HALL_SEAT_SIZE) : numField(o.width, 1),
        height: isSeat ? numField(o.height, HALL_SEAT_SIZE) : numField(o.height, 1),
        rotation: isSeat ? 0 : numField(o.rotation, 0),
        label: typeof o.label === 'string' ? o.label : '',
        ...(hallUuidRaw ? { hallUUID: hallUuidRaw } : {}),
    };

    const optStr = (v: unknown) => {
        if (v == null) {
            return undefined;
        }
        const s = String(v).trim();
        return s.length ? s : undefined;
    };

    if (type === 'seat') {
        const seatObj = o.seat as Record<string, unknown> | undefined;
        const sn = optStr(seatObj?.number ?? o.seatNumber);
        const sr = optStr(seatObj?.row ?? o.seatRow);
        if (sn !== undefined) {
            el.seatNumber = sn;
        }
        if (sr !== undefined) {
            el.seatRow = sr;
        }
        const sec = optStr(o.sector) ?? optStr(o.sectorId);
        if (sec !== undefined) {
            el.sector = sec;
        }
    }

    return el;
};

export const normalizeHallElementsFromJson = (raw: unknown): any[] => {
    if (!Array.isArray(raw)) {
        return [];
    }
    return raw.map(normalizeHallElementFromJson).filter((e): e is any => e != null && e.type != null && ['seat', 'table', 'stage', 'wall', 'sector', 'zone'].includes(e.type));
};

/** Массив `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 === '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 } : {}),
        };
    }
    return null;
}

/** Нормализованный плоский список элементов редактора из значения поля / legacy-объекта. */
export function hallElementsFromInitValue(value: unknown): any[] {
    if (value == null) {
        return [];
    }
    if (Array.isArray(value)) {
        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 sortHallElementsByPaintOrder(reconcileSeatSectorsLayout(items));
    }
    if (typeof value === 'object') {
        const h = value as Record<string, unknown>;
        if (hallSplitCountLegacy(h) > 0) {
            return sortHallElementsByPaintOrder(reconcileSeatSectorsLayout(apiHallToEditorElements(h as any)));
        }
        const elements = h.elements;
        if (Array.isArray(elements) && elements.length) {
            return sortHallElementsByPaintOrder(reconcileSeatSectorsLayout(normalizeHallElementsFromJson(elements)));
        }
    }
    return [];
}

/** Редактор → `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 === 'zone') {
        base.width = el.width;
        base.height = el.height;
        return base;
    }
    return base;
}

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

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