// Game room and state management
const { PhysicsEngine } = require('./physics');
const { CLASSIC_STADIUM } = require('./stadium');

const TICK_RATE = 60;
const TICK_INTERVAL = 1000 / TICK_RATE;
const BROADCAST_EVERY = 2; // Send state every N ticks (30fps broadcast)

class Game {
    constructor(roomId, roomName, maxPlayers, password, adminId) {
        this.roomId = roomId;
        this.roomName = roomName;
        this.maxPlayers = maxPlayers || 10;
        this.password = password || null;
        this.adminId = adminId;
        this.stadium = CLASSIC_STADIUM;
        this.physics = new PhysicsEngine(this.stadium);
        this.scoreLimit = 3;
        this.timeLimit = 3 * 60; // 3 minutes in seconds

        // Players in this room
        this.players = new Map(); // id -> player data

        // Game state
        this.state = 'waiting'; // waiting, kickoff, playing, goal, ended
        this.score = { red: 0, blue: 0 };
        this.gameTime = 0; // in ticks
        this.goalPauseTimer = 0;
        this.gameDiscs = []; // All game discs including ball and players
        this.ball = null;
        this.tickInterval = null;
        this.lastGoalTeam = null;
        this.chatMessages = [];
        this.broadcastCounter = 0;
        this.lastKickTime = 0;
    }

    addPlayer(id, name, ws) {
        const player = {
            id,
            name: name || `Player${this.players.size + 1}`,
            team: 'spectator',
            ws,
            discIndex: -1
        };
        this.players.set(id, player);
        this.broadcastChat(null, 'Sistem', `${player.name} odaya katıldı.`);
        return player;
    }

    removePlayer(id) {
        const player = this.players.get(id);
        if (!player) return;

        this.broadcastChat(null, 'Sistem', `${player.name} odadan ayrıldı.`);
        this.players.delete(id);

        // If admin left, assign new admin
        if (id === this.adminId) {
            const remaining = Array.from(this.players.keys());
            if (remaining.length > 0) {
                this.adminId = remaining[0];
            }
        }

        // If game is running, remove player disc
        if (this.state !== 'waiting') {
            this.rebuildDiscs();
        }
    }

    setTeam(playerId, team) {
        const player = this.players.get(playerId);
        if (!player) return false;
        if (!['red', 'blue', 'spectator'].includes(team)) return false;
        player.team = team;
        // If game is running, rebuild discs to add/remove player
        if (this.state !== 'waiting') {
            this.rebuildDiscs();
        }
        return true;
    }

    getTeamPlayers(team) {
        return Array.from(this.players.values()).filter(p => p.team === team);
    }

    canStart() {
        return true;
    }

    startGame() {
        if (!this.canStart()) return false;

        this.state = 'kickoff';
        this.score = { red: 0, blue: 0 };
        this.gameTime = 0;
        this.lastGoalTeam = null;

        this.rebuildDiscs();
        this.startTick();
        return true;
    }

    stopGame() {
        this.state = 'waiting';
        if (this.tickInterval) {
            clearInterval(this.tickInterval);
            this.tickInterval = null;
        }
        this.gameDiscs = [];
        this.ball = null;
    }

    rebuildDiscs() {
        this.gameDiscs = [];

        // Create ball
        this.ball = this.physics.createBall();
        this.ball.x = 0;
        this.ball.y = 0;
        this.ball.vx = 0;
        this.ball.vy = 0;
        this.gameDiscs.push(this.ball);

        // Create player discs
        let redIndex = 0;
        let blueIndex = 0;

        for (const player of this.players.values()) {
            if (player.team === 'red') {
                const disc = this.physics.createPlayer('red', redIndex);
                player.discIndex = this.gameDiscs.length;
                this.gameDiscs.push(disc);
                redIndex++;
            } else if (player.team === 'blue') {
                const disc = this.physics.createPlayer('blue', blueIndex);
                player.discIndex = this.gameDiscs.length;
                this.gameDiscs.push(disc);
                blueIndex++;
            } else {
                player.discIndex = -1;
            }
        }
    }

    resetPositions() {
        // Reset ball
        this.ball.x = 0;
        this.ball.y = 0;
        this.ball.vx = 0;
        this.ball.vy = 0;

        // Reset player positions
        let redIndex = 0;
        let blueIndex = 0;

        for (const player of this.players.values()) {
            if (player.discIndex >= 0 && player.discIndex < this.gameDiscs.length) {
                const disc = this.gameDiscs[player.discIndex];
                const spawns = disc.team === 'red' ? this.stadium.redSpawnPoints : this.stadium.blueSpawnPoints;
                const idx = disc.team === 'red' ? redIndex++ : blueIndex++;
                const spawn = spawns[idx % spawns.length];
                disc.x = spawn.x;
                disc.y = spawn.y;
                disc.vx = 0;
                disc.vy = 0;
            }
        }
    }

    startTick() {
        if (this.tickInterval) clearInterval(this.tickInterval);

        this.tickInterval = setInterval(() => {
            this.tick();
        }, TICK_INTERVAL);
    }

    tick() {
        if (this.state === 'waiting' || this.state === 'ended') return;

        if (this.state === 'goal') {
            this.goalPauseTimer--;
            if (this.goalPauseTimer <= 0) {
                // Check if game is over
                if (this.score.red >= this.scoreLimit || this.score.blue >= this.scoreLimit) {
                    this.state = 'ended';
                    const winner = this.score.red >= this.scoreLimit ? 'red' : 'blue';
                    this.broadcastEvent('game_end', { winner, score: this.score });
                    setTimeout(() => this.stopGame(), 3000);
                    return;
                }
                this.state = 'kickoff';
                this.resetPositions();
            }
            this.broadcastState();
            return;
        }

        // Game time
        this.gameTime++;

        // Check time limit
        if (this.timeLimit > 0 && this.gameTime >= this.timeLimit * TICK_RATE) {
            this.state = 'ended';
            const winner = this.score.red > this.score.blue ? 'red' :
                this.score.blue > this.score.red ? 'blue' : 'draw';
            this.broadcastEvent('game_end', { winner, score: this.score });
            setTimeout(() => this.stopGame(), 3000);
            return;
        }

        // Set kickoff flag so physics engine knows to enable/disable kickoff barriers
        this.physics.isKickoff = (this.state === 'kickoff');

        // Run physics
        const physicsResult = this.physics.step({ discs: this.gameDiscs });
        const goalResult = physicsResult.goal;

        // Broadcast kick sound if ball was kicked (with a small cooldown to avoid double sounds)
        if (physicsResult.kicked && (this.gameTime - this.lastKickTime > 5)) {
            this.lastKickTime = this.gameTime;
            this.broadcastEvent('kick', {});
        }

        // After first kick, change state from kickoff to playing
        if (this.state === 'kickoff') {
            // Check if ball has moved
            if (Math.abs(this.ball.vx) > 0.01 || Math.abs(this.ball.vy) > 0.01) {
                this.state = 'playing';
            }
        }

        // Check goal
        if (goalResult && this.state === 'playing') {
            this.score[goalResult.scoringTeam]++;
            this.state = 'goal';
            this.goalPauseTimer = TICK_RATE * 2; // 2 second pause
            this.lastGoalTeam = goalResult.scoringTeam;
            this.broadcastEvent('goal', {
                team: goalResult.scoringTeam,
                score: { ...this.score }
            });
        }

        // Broadcast state to all players (throttled)
        this.broadcastCounter++;
        if (this.broadcastCounter >= BROADCAST_EVERY) {
            this.broadcastCounter = 0;
            this.broadcastState();
        }
    }

    handleInput(playerId, input) {
        const player = this.players.get(playerId);
        if (!player || player.discIndex < 0) return;
        if (player.discIndex >= this.gameDiscs.length) return;

        const disc = this.gameDiscs[player.discIndex];
        if (disc && disc.input) {
            disc.input.up = !!input.up;
            disc.input.down = !!input.down;
            disc.input.left = !!input.left;
            disc.input.right = !!input.right;
            disc.input.kick = !!input.kick;
        }
    }

    broadcastState() {
        const msg = this._buildStateMsg();
        for (const player of this.players.values()) {
            try {
                if (player.ws && player.ws.readyState === 1) {
                    player.ws.send(msg);
                }
            } catch (e) { }
        }
    }

    _buildStateMsg() {
        const discs = new Array(this.gameDiscs.length);
        for (let i = 0; i < this.gameDiscs.length; i++) {
            const d = this.gameDiscs[i];
            discs[i] = {
                x: (d.x * 100 + 0.5) | 0,
                y: (d.y * 100 + 0.5) | 0,
                r: d.radius,
                c: d.color,
                b: d.isBall ? 1 : 0,
                t: d.team === 'red' ? 1 : d.team === 'blue' ? 2 : 0,
                k: d.isKicking ? 1 : 0
            };
        }

        const pm = {};
        for (const [id, player] of this.players) {
            if (player.discIndex >= 0) {
                pm[player.discIndex] = { n: player.name, t: player.team, id };
            }
        }

        return JSON.stringify({
            type: 'game_state',
            data: {
                s: this.state,
                sc: this.score,
                gt: this.gameTime,
                tl: this.timeLimit,
                d: discs,
                pm: pm,
                lg: this.lastGoalTeam
            }
        });
    }

    broadcastEvent(eventType, data) {
        const msg = JSON.stringify({ type: eventType, data });
        for (const player of this.players.values()) {
            try {
                if (player.ws && player.ws.readyState === 1) {
                    player.ws.send(msg);
                }
            } catch (e) { }
        }
    }

    broadcastChat(senderId, senderName, message) {
        const chatMsg = {
            senderId,
            senderName,
            message: message.substring(0, 200),
            timestamp: Date.now()
        };
        this.chatMessages.push(chatMsg);
        if (this.chatMessages.length > 100) this.chatMessages.shift();

        const msg = JSON.stringify({ type: 'chat_msg', data: chatMsg });
        for (const player of this.players.values()) {
            try {
                if (player.ws && player.ws.readyState === 1) {
                    player.ws.send(msg);
                }
            } catch (e) { }
        }
    }

    broadcastRoomState() {
        const roomState = this.getRoomState();
        const msg = JSON.stringify({ type: 'room_state', data: roomState });
        for (const player of this.players.values()) {
            try {
                if (player.ws && player.ws.readyState === 1) {
                    player.ws.send(msg);
                }
            } catch (e) { }
        }
    }

    getGameStateSnapshot() {
        const discs = this.gameDiscs.map(d => ({
            x: Math.round(d.x * 100) / 100,
            y: Math.round(d.y * 100) / 100,
            vx: Math.round(d.vx * 100) / 100,
            vy: Math.round(d.vy * 100) / 100,
            radius: d.radius,
            color: d.color,
            isBall: d.isBall || false,
            team: d.team || null,
            isKicking: d.isKicking || false
        }));

        // Map player names to disc indices
        const playerMap = {};
        for (const [id, player] of this.players) {
            if (player.discIndex >= 0) {
                playerMap[player.discIndex] = { name: player.name, team: player.team, id };
            }
        }

        return {
            state: this.state,
            score: this.score,
            gameTime: this.gameTime,
            timeLimit: this.timeLimit,
            discs,
            playerMap,
            lastGoalTeam: this.lastGoalTeam
        };
    }

    getRoomState() {
        const players = [];
        for (const [id, p] of this.players) {
            players.push({
                id,
                name: p.name,
                team: p.team,
                isAdmin: id === this.adminId
            });
        }

        return {
            roomId: this.roomId,
            roomName: this.roomName,
            state: this.state,
            players,
            adminId: this.adminId,
            score: this.score,
            maxPlayers: this.maxPlayers,
            hasPassword: !!this.password,
            scoreLimit: this.scoreLimit,
            timeLimit: this.timeLimit
        };
    }

    getRoomListEntry() {
        return {
            roomId: this.roomId,
            roomName: this.roomName,
            playerCount: this.players.size,
            maxPlayers: this.maxPlayers,
            state: this.state,
            hasPassword: !!this.password
        };
    }
}

module.exports = { Game, TICK_RATE };
