const GENERADOR_W = 8;

const NOMBRES_GEN = [
    'Periferia bajo lluvia',
    'Polígono al amanecer',
    'Las Ramblas a las 3am',
    'Centro un domingo de partido',
    'Barrio nuevo sin nombre',
    'Casco antiguo en agosto',
    'Calle de la Pasta Madre',
    'Avenida del WiFi Lento',
    'Bulevar de los Patinetes',
    'Plaza del Vermú Caro',
    'Calle del Despertador',
    'Suburbio con boda gitana',
    'Gran Vía a las dos',
    'Plaza de los Comercios Cerrados',
    'Callejón del Wallapop',
    'Avenida del Comulgar',
    'Bulevar del Botellón',
    'Plaza del Mercadona Cerrado',
    'Polígono Industrial Quevedo',
    'Calle del Hilo del Vecino'
];

const DESCRIPCIONES_GEN = [
    'Más fauna, calles más estrechas, menos paciencia.',
    'La ciudad ya sabe quién eres. Cuidado con las esquinas.',
    'Si te paran, paciencia. Si te encierran, sprint.',
    'Has llegado más lejos que casi nadie. La ciudad escala contigo.',
    'Cada callejón es una promesa o una emboscada.',
    'Todo el mundo quiere algo de ti.',
    'Hoy hay más farándula que vecinos.'
];

function _genRand(seed) {
    let s = seed % 2147483647;
    if (s <= 0) s += 2147483646;
    return function() {
        s = (s * 16807) % 2147483647;
        return (s - 1) / 2147483646;
    };
}

function _arquetiposPorIdx(idx) {
    const pool = ['trilero', 'flyers', 'ong', 'rosas', 'turistas'];
    if (idx >= 3) pool.push('amego', 'amego');
    if (idx >= 4) pool.push('mantero', 'cuponero', 'amego');
    if (idx >= 5) pool.push('selfies', 'carterista', 'amego');
    if (idx >= 6) pool.push('discoteca', 'amego');
    if (idx >= 7) pool.push('predicador', 'borracho', 'mendigo', 'charlatan', 'cantante', 'amego');
    if (idx >= 11) pool.push('scooter');
    return pool;
}

function _ambientePorIdx(idx) {
    const ciclo = idx % 6;
    if (ciclo < 2) return 'dia';
    if (ciclo < 4) return 'tarde';
    return 'noche';
}

function _perpendiculares(dir) {
    if (dir[0] === 0) return [[1, 0], [-1, 0]];
    return [[0, 1], [0, -1]];
}

function _rectsOverlap(a, b) {
    return a.minX < b.maxX && a.maxX > b.minX && a.minZ < b.maxZ && a.maxZ > b.minZ;
}

function _fitsSinColision(newRect, path) {
    for (const c of path) {
        if (_rectsOverlap(newRect, c.rect)) return false;
    }
    return true;
}

function _placeStraight(prev, L) {
    const r = prev.rect;
    const d = prev.dir;
    if (d[1] === -1) return { minX: r.minX, maxX: r.maxX, minZ: r.minZ - L, maxZ: r.minZ };
    if (d[1] === 1)  return { minX: r.minX, maxX: r.maxX, minZ: r.maxZ, maxZ: r.maxZ + L };
    if (d[0] === 1)  return { minX: r.maxX, maxX: r.maxX + L, minZ: r.minZ, maxZ: r.maxZ };
    return { minX: r.minX - L, maxX: r.minX, minZ: r.minZ, maxZ: r.maxZ };
}

function _placePlaza(prev, L, ancho) {
    const r = prev.rect;
    const d = prev.dir;
    if (d[0] === 0) {
        const cx = (r.minX + r.maxX) / 2;
        const minX = cx - ancho / 2;
        const maxX = cx + ancho / 2;
        if (d[1] === -1) return { minX, maxX, minZ: r.minZ - L, maxZ: r.minZ };
        return { minX, maxX, minZ: r.maxZ, maxZ: r.maxZ + L };
    }
    const cz = (r.minZ + r.maxZ) / 2;
    const minZ = cz - ancho / 2;
    const maxZ = cz + ancho / 2;
    if (d[0] === 1) return { minX: r.maxX, maxX: r.maxX + L, minZ, maxZ };
    return { minX: r.minX - L, maxX: r.minX, minZ, maxZ };
}

function _placeTurn(prev, newDir, L, W) {
    const r = prev.rect;
    const d = prev.dir;
    if (d[1] === -1) {
        const minZ = r.minZ - W, maxZ = r.minZ;
        if (newDir[0] === 1)  return { minX: r.minX, maxX: r.minX + L, minZ, maxZ };
        return { minX: r.maxX - L, maxX: r.maxX, minZ, maxZ };
    }
    if (d[1] === 1) {
        const minZ = r.maxZ, maxZ = r.maxZ + W;
        if (newDir[0] === 1)  return { minX: r.minX, maxX: r.minX + L, minZ, maxZ };
        return { minX: r.maxX - L, maxX: r.maxX, minZ, maxZ };
    }
    if (d[0] === 1) {
        const minX = r.maxX - W, maxX = r.maxX;
        if (newDir[1] === -1) return { minX, maxX, minZ: r.minZ - L, maxZ: r.minZ };
        return { minX, maxX, minZ: r.maxZ, maxZ: r.maxZ + L };
    }
    const minX = r.minX, maxX = r.minX + W;
    if (newDir[1] === -1) return { minX, maxX, minZ: r.minZ - L, maxZ: r.minZ };
    return { minX, maxX, minZ: r.maxZ, maxZ: r.maxZ + L };
}

function _placeCallejon(prev, side, L, depth) {
    const r = prev.rect;
    const d = prev.dir;
    if (d[1] === -1) {
        const cz = (r.minZ + r.maxZ) / 2;
        const minZ = cz - depth / 2, maxZ = cz + depth / 2;
        if (side === 'left')  return { minX: r.maxX, maxX: r.maxX + L, minZ, maxZ };
        return { minX: r.minX - L, maxX: r.minX, minZ, maxZ };
    }
    if (d[1] === 1) {
        const cz = (r.minZ + r.maxZ) / 2;
        const minZ = cz - depth / 2, maxZ = cz + depth / 2;
        if (side === 'left')  return { minX: r.minX - L, maxX: r.minX, minZ, maxZ };
        return { minX: r.maxX, maxX: r.maxX + L, minZ, maxZ };
    }
    if (d[0] === 1) {
        const cx = (r.minX + r.maxX) / 2;
        const minX = cx - depth / 2, maxX = cx + depth / 2;
        if (side === 'left')  return { minX, maxX, minZ: r.maxZ, maxZ: r.maxZ + L };
        return { minX, maxX, minZ: r.minZ - L, maxZ: r.minZ };
    }
    const cx = (r.minX + r.maxX) / 2;
    const minX = cx - depth / 2, maxX = cx + depth / 2;
    if (side === 'left')  return { minX, maxX, minZ: r.minZ - L, maxZ: r.minZ };
    return { minX, maxX, minZ: r.maxZ, maxZ: r.maxZ + L };
}

function _placeRamaBifurcacion(approach, branch, L, W) {
    const r = approach.rect;
    const d = approach.dir;
    if (d[1] === -1) {
        if (branch === 'A') return { minX: r.minX, maxX: r.minX + W, minZ: r.minZ - L, maxZ: r.minZ };
        return { minX: r.maxX - W, maxX: r.maxX, minZ: r.minZ - L, maxZ: r.minZ };
    }
    if (d[1] === 1) {
        if (branch === 'A') return { minX: r.minX, maxX: r.minX + W, minZ: r.maxZ, maxZ: r.maxZ + L };
        return { minX: r.maxX - W, maxX: r.maxX, minZ: r.maxZ, maxZ: r.maxZ + L };
    }
    if (d[0] === 1) {
        if (branch === 'A') return { minX: r.maxX, maxX: r.maxX + L, minZ: r.maxZ - W, maxZ: r.maxZ };
        return { minX: r.maxX, maxX: r.maxX + L, minZ: r.minZ, maxZ: r.minZ + W };
    }
    if (branch === 'A') return { minX: r.minX - L, maxX: r.minX, minZ: r.maxZ - W, maxZ: r.maxZ };
    return { minX: r.minX - L, maxX: r.minX, minZ: r.minZ, maxZ: r.minZ + W };
}

function _placeHomeBifurcacion(approach, baR, bbR, L) {
    const d = approach.dir;
    const minX = Math.min(baR.minX, bbR.minX);
    const maxX = Math.max(baR.maxX, bbR.maxX);
    const minZ = Math.min(baR.minZ, bbR.minZ);
    const maxZ = Math.max(baR.maxZ, bbR.maxZ);
    if (d[1] === -1) return { minX, maxX, minZ: minZ - L, maxZ: minZ };
    if (d[1] === 1)  return { minX, maxX, minZ: maxZ, maxZ: maxZ + L };
    if (d[0] === 1)  return { minX: maxX, maxX: maxX + L, minZ, maxZ };
    return { minX: minX - L, maxX: minX, minZ, maxZ };
}

function generarNivel(idx) {
    const seed = idx * 9991 + 17;
    const rand = _genRand(seed);
    const W = GENERADOR_W;

    const totalSegs = Math.max(6, Math.min(16, 6 + Math.floor(idx * 0.7)));
    const numNPCs = Math.max(12, Math.min(50, 12 + Math.floor(idx * 2.4)));
    const velocidadMultiplicador = Math.min(1.8, 1.15 + idx * 0.05);
    const arquetipos = _arquetiposPorIdx(idx);
    const ambiente = _ambientePorIdx(idx);

    const path = [];
    const L0 = 16 + Math.floor(rand() * 8);
    path.push({
        id: 'm0',
        type: 'recta',
        rect: { minX: -W / 2, maxX: W / 2, minZ: 6 - L0, maxZ: 6 },
        dir: [0, -1],
        milestone: 0.05
    });

    let mainIdx = 0;
    let alleyCount = 0;
    let plazaCount = 0;
    const lastMainStep = totalSegs - 2;

    for (let step = 1; step <= lastMainStep; step++) {
        const prev = path[mainIdx];

        if (alleyCount < 2 && rand() < 0.30) {
            const side = rand() < 0.5 ? 'left' : 'right';
            const aL = 10;
            const aD = 4;
            const candR = _placeCallejon(prev, side, aL, aD);
            if (_fitsSinColision(candR, path)) {
                path.push({
                    id: 'a' + (alleyCount + 1),
                    type: 'callejon',
                    rect: candR,
                    dir: [...prev.dir],
                    milestone: prev.milestone || 0,
                    powerup: alleyCount === 0 ? 'auriculares' : 'cafe'
                });
                alleyCount++;
            }
        }

        const milestone = step / (lastMainStep + 1);
        let placed = false;

        const wantTurn = rand() < 0.40;
        if (wantTurn) {
            const perps = _perpendiculares(prev.dir);
            const order = rand() < 0.5 ? [0, 1] : [1, 0];
            for (const oi of order) {
                const newDir = perps[oi];
                const L = 16 + Math.floor(rand() * 14);
                const candR = _placeTurn(prev, newDir, L, W);
                if (_fitsSinColision(candR, path)) {
                    path.push({
                        id: 'm' + step,
                        type: 'recta',
                        rect: candR,
                        dir: newDir,
                        milestone: milestone
                    });
                    mainIdx = path.length - 1;
                    placed = true;
                    break;
                }
            }
        }

        if (!placed && plazaCount < 2 && rand() < 0.45) {
            const L = 16 + Math.floor(rand() * 6);
            const ancho = 18 + Math.floor(rand() * 5);
            const candR = _placePlaza(prev, L, ancho);
            if (_fitsSinColision(candR, path)) {
                path.push({
                    id: 'm' + step,
                    type: 'plaza',
                    rect: candR,
                    dir: [...prev.dir],
                    milestone: milestone
                });
                mainIdx = path.length - 1;
                plazaCount++;
                placed = true;
            }
        }

        if (!placed) {
            const candidatos = [
                14 + Math.floor(rand() * 12),
                10 + Math.floor(rand() * 6),
                8
            ];
            for (const L of candidatos) {
                const candR = _placeStraight(prev, L);
                if (_fitsSinColision(candR, path)) {
                    path.push({
                        id: 'm' + step,
                        type: 'recta',
                        rect: candR,
                        dir: [...prev.dir],
                        milestone: milestone
                    });
                    mainIdx = path.length - 1;
                    placed = true;
                    break;
                }
            }
        }

        if (!placed) break;
    }

    const lastMain = path[mainIdx];
    let bifurcado = false;
    if (idx >= 4 && rand() < 0.55) {
        const approachL = 16;
        const approachW = 22;
        const approachR = _placePlaza(lastMain, approachL, approachW);
        if (_fitsSinColision(approachR, path)) {
            const approach = {
                id: 'mp',
                type: 'plaza',
                rect: approachR,
                dir: [...lastMain.dir],
                milestone: 0.85
            };
            path.push(approach);

            const branchL = 14;
            const baR = _placeRamaBifurcacion(approach, 'A', branchL, W);
            const bbR = _placeRamaBifurcacion(approach, 'B', branchL, W);
            if (_fitsSinColision(baR, path) && _fitsSinColision(bbR, path)) {
                path.push({ id: 'ba', type: 'recta', rect: baR, dir: [...approach.dir], milestone: 0.92, branch: 'A' });
                path.push({ id: 'bb', type: 'recta', rect: bbR, dir: [...approach.dir], milestone: 0.92, branch: 'B' });

                const homeR = _placeHomeBifurcacion(approach, baR, bbR, 12);
                if (_fitsSinColision(homeR, path)) {
                    path.push({ id: 'mh', type: 'recta', rect: homeR, dir: [...approach.dir], milestone: 1.0, isHome: true });
                    bifurcado = true;
                }
            }
        }
    }

    if (!bifurcado) {
        const homeL = 14 + Math.floor(rand() * 6);
        const homeR = _placeStraight(path[mainIdx], homeL);
        if (_fitsSinColision(homeR, path)) {
            path.push({ id: 'mh', type: 'recta', rect: homeR, dir: [...path[mainIdx].dir], milestone: 1.0, isHome: true });
        } else {
            const fallback = _placeStraight(path[mainIdx], 10);
            path.push({ id: 'mh', type: 'recta', rect: fallback, dir: [...path[mainIdx].dir], milestone: 1.0, isHome: true });
        }
    }

    const homeRect = path[path.length - 1].rect;
    const home = {
        x: (homeRect.minX + homeRect.maxX) / 2,
        z: (homeRect.minZ + homeRect.maxZ) / 2
    };

    return {
        id: 1000 + idx,
        nombre: NOMBRES_GEN[idx % NOMBRES_GEN.length],
        descripcion: DESCRIPCIONES_GEN[idx % DESCRIPCIONES_GEN.length],
        ambiente: ambiente,
        velocidadMultiplicador: velocidadMultiplicador,
        spawn: { x: 0, z: 6 },
        home: home,
        path: path,
        numNPCs: numNPCs,
        arquetipos: arquetipos
    };
}
