/** Геометрия полигонального сектора (`sector_polygon`). */

export type HallPolygonPoint = { x: number; y: number };

/** Тип сектора с посадочными местами (прямоугольник или полигон). */
export function isHallSeatSectorType(type: string | undefined | null): boolean {
    return type === 'sector' || type === 'sector_polygon';
}

export function isHallNamedSectorType(type: string | undefined | null): boolean {
    return type === 'sector' || type === 'sector_polygon' || type === 'zone';
}

/** Плоский `[x1,y1,x2,y2,…]` → точки. */
export function hallPolygonFlatToPoints(flat: number[] | undefined | null): HallPolygonPoint[] {
    if (!Array.isArray(flat) || flat.length < 6) {
        return [];
    }
    const out: HallPolygonPoint[] = [];
    for (let i = 0; i + 1 < flat.length; i += 2) {
        const x = Number(flat[i]);
        const y = Number(flat[i + 1]);
        if (!Number.isFinite(x) || !Number.isFinite(y)) {
            continue;
        }
        out.push({ x, y });
    }
    return out.length >= 3 ? out : [];
}

export function hallPolygonPointsToFlat(points: HallPolygonPoint[]): number[] {
    const flat: number[] = [];
    points.forEach((p) => {
        flat.push(p.x, p.y);
    });
    return flat;
}

/** Абсолютные вершины: локальные `points` + `(x,y)` с учётом `rotation` вокруг `(x,y)`. */
export function hallPolygonAbsolutePoints(el: {
    x: number;
    y: number;
    points?: number[];
    rotation?: number;
}): HallPolygonPoint[] {
    const rel = hallPolygonFlatToPoints(el.points);
    const rot = el.rotation ?? 0;
    if (!rot) {
        return rel.map((p) => ({ x: el.x + p.x, y: el.y + p.y }));
    }
    const rad = (rot * Math.PI) / 180;
    const cos = Math.cos(rad);
    const sin = Math.sin(rad);
    return rel.map((p) => ({
        x: el.x + p.x * cos - p.y * sin,
        y: el.y + p.x * sin + p.y * cos,
    }));
}

/** AABB локальных (неповёрнутых) точек → width/height. */
export function hallPolygonLocalSize(points: number[] | undefined | null): {
    width: number;
    height: number;
} {
    const rel = hallPolygonFlatToPoints(points);
    if (!rel.length) {
        return { width: 1, height: 1 };
    }
    let minX = rel[0]!.x;
    let minY = rel[0]!.y;
    let maxX = minX;
    let maxY = minY;
    for (let i = 1; i < rel.length; i += 1) {
        const p = rel[i]!;
        minX = Math.min(minX, p.x);
        minY = Math.min(minY, p.y);
        maxX = Math.max(maxX, p.x);
        maxY = Math.max(maxY, p.y);
    }
    return {
        width: Math.max(1, maxX - minX),
        height: Math.max(1, maxY - minY),
    };
}

export function hallPolygonBBox(points: HallPolygonPoint[]): {
    x: number;
    y: number;
    width: number;
    height: number;
} | null {
    if (points.length < 3) {
        return null;
    }
    let minX = points[0]!.x;
    let minY = points[0]!.y;
    let maxX = minX;
    let maxY = minY;
    for (let i = 1; i < points.length; i += 1) {
        const p = points[i]!;
        minX = Math.min(minX, p.x);
        minY = Math.min(minY, p.y);
        maxX = Math.max(maxX, p.x);
        maxY = Math.max(maxY, p.y);
    }
    return {
        x: minX,
        y: minY,
        width: Math.max(1, maxX - minX),
        height: Math.max(1, maxY - minY),
    };
}

/** Из абсолютных вершин → `x/y/width/height` + относительные `points`. */
export function hallPolygonLayoutFromAbsolute(absPoints: HallPolygonPoint[]): {
    x: number;
    y: number;
    width: number;
    height: number;
    points: number[];
    rotation: number;
} | null {
    const bbox = hallPolygonBBox(absPoints);
    if (!bbox) {
        return null;
    }
    const relative = absPoints.map((p) => ({
        x: p.x - bbox.x,
        y: p.y - bbox.y,
    }));
    return {
        x: bbox.x,
        y: bbox.y,
        width: bbox.width,
        height: bbox.height,
        points: hallPolygonPointsToFlat(relative),
        rotation: 0,
    };
}

/** Дефолтный полигон внутри bbox (свободная форма, не прямоугольник). */
export function hallPolygonDefaultRelativePoints(width: number, height: number): number[] {
    const w = Number.isFinite(width) && width > 0 ? width : 260;
    const h = Number.isFinite(height) && height > 0 ? height : 160;
    return [
        w * 0.18, 0,
        w * 0.82, 0,
        w, h * 0.42,
        w * 0.55, h,
        0, h * 0.58,
    ];
}

export function scaleHallPolygonRelativePoints(
    points: number[],
    scaleX: number,
    scaleY: number,
): number[] {
    const sx = Number.isFinite(scaleX) ? scaleX : 1;
    const sy = Number.isFinite(scaleY) ? scaleY : 1;
    const next: number[] = [];
    for (let i = 0; i + 1 < points.length; i += 2) {
        next.push(points[i]! * sx, points[i + 1]! * sy);
    }
    return next;
}

/** Ray casting: точка внутри полигона (абсолютные вершины). */
export function pointInHallPolygon(
    px: number,
    py: number,
    absPoints: HallPolygonPoint[],
): boolean {
    if (absPoints.length < 3) {
        return false;
    }
    let inside = false;
    for (let i = 0, j = absPoints.length - 1; i < absPoints.length; j = i++) {
        const xi = absPoints[i]!.x;
        const yi = absPoints[i]!.y;
        const xj = absPoints[j]!.x;
        const yj = absPoints[j]!.y;
        const intersect = ((yi > py) !== (yj > py))
            && (px < ((xj - xi) * (py - yi)) / ((yj - yi) || Number.EPSILON) + xi);
        if (intersect) {
            inside = !inside;
        }
    }
    return inside;
}

export function seatCenterInHallSector(seat: { x: number; y: number }, sector: any): boolean {
    if (sector?.type === 'sector_polygon') {
        return pointInHallPolygon(seat.x, seat.y, hallPolygonAbsolutePoints(sector));
    }
    if (sector?.type === 'sector') {
        return seat.x >= sector.x
            && seat.x <= sector.x + sector.width
            && seat.y >= sector.y
            && seat.y <= sector.y + sector.height;
    }
    return false;
}

function readableEdgeAngleDeg(dx: number, dy: number): number {
    let angle = (Math.atan2(dy, dx) * 180) / Math.PI;
    if (angle > 90) {
        angle -= 180;
    }
    if (angle <= -90) {
        angle += 180;
    }
    return angle;
}

/** Подпись у визуально верхнего ребра полигона. */
export function hallPolygonLabelLayout(params: {
    absPoints: HallPolygonPoint[];
    fontSize?: number;
    gap?: number;
}): {
    x: number;
    y: number;
    rotation: number;
    width: number;
    offsetX: number;
    offsetY: number;
    fontSize: number;
} | null {
    const pts = params.absPoints;
    if (pts.length < 3) {
        return null;
    }
    const fontSize = params.fontSize ?? 14;
    const gap = params.gap ?? Math.max(10, Math.round(fontSize * 0.85));

    let best: {
        a: HallPolygonPoint;
        b: HallPolygonPoint;
        midX: number;
        midY: number;
        len: number;
    } | null = null;

    for (let i = 0; i < pts.length; i += 1) {
        const a = pts[i]!;
        const b = pts[(i + 1) % pts.length]!;
        const midX = (a.x + b.x) / 2;
        const midY = (a.y + b.y) / 2;
        const len = Math.hypot(b.x - a.x, b.y - a.y);
        if (
            !best
            || midY < best.midY - 1e-6
            || (Math.abs(midY - best.midY) <= 1e-6 && len > best.len)
        ) {
            best = { a, b, midX, midY, len };
        }
    }
    if (!best) {
        return null;
    }

    const centerX = pts.reduce((sum, p) => sum + p.x, 0) / pts.length;
    const centerY = pts.reduce((sum, p) => sum + p.y, 0) / pts.length;
    let nx = best.midX - centerX;
    let ny = best.midY - centerY;
    const nLen = Math.hypot(nx, ny) || 1;
    nx /= nLen;
    ny /= nLen;

    const labelWidth = Math.max(48, best.len * 0.92);
    const angle = readableEdgeAngleDeg(best.b.x - best.a.x, best.b.y - best.a.y);

    return {
        x: best.midX + nx * gap,
        y: best.midY + ny * gap,
        rotation: angle,
        width: labelWidth,
        offsetX: labelWidth / 2,
        offsetY: fontSize / 2,
        fontSize,
    };
}

/** Расстояние до первой точки — для замыкания при рисовании. */
export function hallPolygonCloseSnapDistance(): number {
    return 14;
}
