import type { Canvas, Layer } from '@/types/layout';
import { resolveAnchors, type Rect } from './anchors';
import { isEffectivelyVisible } from './hierarchy';

export type SnapTargets = { x: number[]; y: number[] };

export type SnapResult = {
    dx: number;
    dy: number;
    guides: { x: number[]; y: number[] };
};

export function computeSnapTargets(
    canvas: Canvas,
    layers: Layer[],
    excludeIds: Set<string>,
): SnapTargets {
    const x: number[] = [0, canvas.width / 2, canvas.width];
    const y: number[] = [0, canvas.height / 2, canvas.height];
    for (const layer of layers) {
        if (excludeIds.has(layer.id)) continue;
        if (!isEffectivelyVisible(layer, layers)) continue;
        if (layer.type === 'group') continue;
        const r = resolveAnchors(layer.anchors, canvas);
        x.push(r.x, r.x + r.width / 2, r.x + r.width);
        y.push(r.y, r.y + r.height / 2, r.y + r.height);
    }
    return { x, y };
}

export function computeSnap(
    rect: Rect,
    targets: SnapTargets,
    threshold: number,
): SnapResult {
    const candidatesX = [rect.x, rect.x + rect.width / 2, rect.x + rect.width];
    const candidatesY = [rect.y, rect.y + rect.height / 2, rect.y + rect.height];
    const bestX = findNearest(candidatesX, targets.x, threshold);
    const bestY = findNearest(candidatesY, targets.y, threshold);
    return {
        dx: bestX?.delta ?? 0,
        dy: bestY?.delta ?? 0,
        guides: {
            x: bestX ? [bestX.target] : [],
            y: bestY ? [bestY.target] : [],
        },
    };
}

function findNearest(
    candidates: number[],
    targets: number[],
    threshold: number,
): { delta: number; target: number } | null {
    let best: { delta: number; target: number } | null = null;
    for (const c of candidates) {
        for (const t of targets) {
            const d = t - c;
            const abs = Math.abs(d);
            if (abs > threshold) continue;
            if (!best || abs < Math.abs(best.delta)) {
                best = { delta: d, target: t };
            }
        }
    }
    return best;
}
