import pool from '../db.js';
import { getSectorSettings } from './config.service.js';

/**
 * Hilfsfunktion: Generiert eine zufällige Ganzzahl zwischen min und max (inklusive).
 * 
 * @param {number} min - Minimum
 * @param {number} max - Maximum
 * 
 * @returns {number} Zufällige Ganzzahl
 */
function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

/**
 * Ermittelt die nächsten freien Koordinaten (Galaxie, System, Position) für einen neuen Spieler.
 * Jeder Suchlauf startet bei 0 und springt zufällig vorwärts. Fällt die Suche aus dem
 * Sektor, greift ein Notfall-Drop, der die restlichen Lücken füllt und bei < 100 Plätzen
 * die Schließung des Sektors anordnet.
 * 
 * @returns {Promise<{galaxy: number, system: number, position: number, triggerSectorClose: boolean}>}
 */
export const findNextAvailableStarterCoordinates = async () => {
    const settings = getSectorSettings();
    const starterConfig = settings.starter_zone;
    const universeConfig = settings.universe_dimensions;
    const startPositions = starterConfig.fixed_start_positions; // z.B. [6, 10]
    const client = await pool.connect();

    try {
        let currentGalaxy = 1;
        let currentSystem = 0; // Start bei 0. Der erste Leap bringt uns direkt ins erste gültige System.
        let foundCoordinates = null;
        let triggerSectorClose = false;

        // REGULÄRE SCHLEIFE: Wir starten bei 0 und springen iterativ vorwärts
        while (!foundCoordinates) {
            const leap = getRandomInt(starterConfig.leap_min_systems, starterConfig.leap_max_systems);
            currentSystem += leap;

            if (currentSystem > universeConfig.max_systems_per_galaxy) {
                currentGalaxy += 1;
                currentSystem = currentSystem - universeConfig.max_systems_per_galaxy;
            }

            if (currentGalaxy > universeConfig.max_galaxies) {
                break;
            }

            const systemCheckQuery = await client.query(`
                SELECT position 
                FROM planets 
                WHERE galaxy = $1 AND system = $2 AND position = ANY($3)
            `, [currentGalaxy, currentSystem, startPositions]);

            const occupiedPositions = systemCheckQuery.rows.map(row => row.position);

            const freePosition = startPositions.find(pos => !occupiedPositions.includes(pos));

            if (freePosition) {
                foundCoordinates = {
                    galaxy: currentGalaxy,
                    system: currentSystem,
                    position: freePosition,
                    triggerSectorClose: false // Im regulären Betrieb bleibt der Sektor offen
                };
            }
        }

        // NOTFALL-DROP: Die reguläre Suche ist am Ende des Universums angekommen
        if (!foundCoordinates) {
            console.log('[PlanetService] Suche aus Sektor gefallen. Starte Notfall-Drop Scan...');

            // Wir zählen alle belegten Startplätze im GESAMTEN Sektor
            const occupiedCountQuery = await client.query(`
                SELECT COUNT(*) as count 
                FROM planets 
                WHERE position = ANY($1)
            `, [startPositions]);

            const occupiedCount = parseInt(occupiedCountQuery.rows[0].count, 10);
            
            // Mathematisch maximale Anzahl an Startplätzen im Sektor berechnen
            const totalStarterSlots = universeConfig.max_galaxies * universeConfig.max_systems_per_galaxy * startPositions.length;
            const freeSlots = totalStarterSlots - occupiedCount;

            // Absolute Notbremse: Sollte theoretisch durch unseren 100er-Puffer nie erreicht werden,
            // aber schützt die Datenbank vor Endlosschleifen oder Null-Referenzen.
            if (freeSlots <= 0) {
                console.error('[PlanetService] Absolute Notbremse: Sektor ist komplett voll!');
                const error = new Error("Sector is completely full");
                error.code = 'ERR_SECTOR_FULL';
                throw error;
            }

            // UX-SCHUTZ: Wenn wir die Grenze von 100 freien Plätzen erreichen,
            // ordnen wir die "weiche" Schließung an. Der aktuelle Spieler bekommt seinen Platz!
            if (freeSlots <= 100) {
                console.warn(`[PlanetService] Sektor-Limit erreicht (${freeSlots} Plätze übrig). Trigger Sektor-Schließung!`);
                triggerSectorClose = true;
            }

            // LÜCKEN FÜLLEN: Wir suchen das allererste System (von 1:1 an aufwärts),
            // in dem noch nicht alle Start-Slots belegt sind.
            const firstFreeSystemQuery = await client.query(`
                SELECT galaxy, system
                FROM (
                    SELECT g.galaxy, s.system
                    FROM generate_series(1, $1) g(galaxy)
                    CROSS JOIN generate_series(1, $2) s(system)
                ) all_systems
                LEFT JOIN (
                    SELECT galaxy, system, COUNT(*) as occupied_slots
                    FROM planets
                    WHERE position = ANY($3)
                    GROUP BY galaxy, system
                ) occupied ON all_systems.galaxy = occupied.galaxy AND all_systems.system = occupied.system
                WHERE COALESCE(occupied.occupied_slots, 0) < $4
                ORDER BY all_systems.galaxy ASC, all_systems.system ASC
                LIMIT 1
            `, [
                universeConfig.max_galaxies, 
                universeConfig.max_systems_per_galaxy, 
                startPositions, 
                startPositions.length
            ]);

            const fallbackGalaxy = firstFreeSystemQuery.rows[0].galaxy;
            const fallbackSystem = firstFreeSystemQuery.rows[0].system;

            // Im gefundenen Fallback-System prüfen wir, welche der Positionen (z.B. 6 oder 10) konkret frei ist
            const checkFallbackPos = await client.query(`
                SELECT position 
                FROM planets 
                WHERE galaxy = $1 AND system = $2 AND position = ANY($3)
            `, [fallbackGalaxy, fallbackSystem, startPositions]);

            const fbOccupied = checkFallbackPos.rows.map(row => row.position);
            const fbFreePos = startPositions.find(pos => !fbOccupied.includes(pos));

            foundCoordinates = {
                galaxy: fallbackGalaxy,
                system: fallbackSystem,
                position: fbFreePos,
                triggerSectorClose: triggerSectorClose // Gibt die Warnung an den Controller weiter
            };
        }

        return foundCoordinates;

    } catch (error) {
        console.error('[PlanetService] Fehler bei der Koordinaten-Ermittlung:', error);
        throw error;
    } finally {
        client.release();
    }
};

/**
 * Berechnet die exakte Anzahl der noch freien Startplätze im gesamten Sektor.
 * Wird von der Lobby für den nächtlichen Cronjob zur Sektor-Öffnung abgerufen.
 * 
 * @returns {Promise<number>} Anzahl der freien Plätze
 */
export const getFreeStarterSlotsCount = async () => {
    const settings = getSectorSettings();
    const starterConfig = settings.starter_zone;
    const universeConfig = settings.universe_dimensions;
    const startPositions = starterConfig.fixed_start_positions;

    const client = await pool.connect();
    
    try {
        const occupiedCountQuery = await client.query(`
            SELECT COUNT(*) as count 
            FROM planets 
            WHERE position = ANY($1)
        `, [startPositions]);

        const occupiedCount = parseInt(occupiedCountQuery.rows[0].count, 10);
        
        const totalStarterSlots = universeConfig.max_galaxies * universeConfig.max_systems_per_galaxy * startPositions.length;
        
        return totalStarterSlots - occupiedCount;

    } catch (error) {
        console.error('[PlanetService] Fehler bei der Berechnung der freien Startplätze:', error);
        throw error;
    } finally {
        client.release();
    }
};