import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'
import { setupWebSocketHandler } from '../../src/websocket-handler.js'
import { punteggio } from '../../src/gameState.js'
import { EventEmitter } from 'events'

// Il punteggio si ricava dalla striscia, non è memorizzato nello stato.
const punt = (state) => punteggio(state.sp.striscia)

class MockWebSocket extends EventEmitter {
    constructor() {
        super()
        this.readyState = 1
    }
    send = vi.fn()
    terminate = vi.fn()
}

class MockWebSocketServer extends EventEmitter {
    clients = new Set()
}

function connectAndRegister(wss, role) {
    const ws = new MockWebSocket()
    wss.emit('connection', ws)
    wss.clients.add(ws)
    ws.emit('message', JSON.stringify({ type: 'register', role }))
    ws.send.mockClear()
    return ws
}

describe('Stress Test WebSocket', () => {
    let wss
    let handler

    beforeEach(() => {
        wss = new MockWebSocketServer()
        handler = setupWebSocketHandler(wss)
    })

    afterEach(() => {
        vi.restoreAllMocks()
    })

    it('dovrebbe gestire 50 client display connessi simultaneamente', () => {
        const displays = []
        for (let i = 0; i < 50; i++) {
            displays.push(connectAndRegister(wss, 'display'))
        }

        expect(handler.getClients().size).toBe(50)

        // Un controller invia un'azione
        const controller = connectAndRegister(wss, 'controller')
        controller.emit('message', JSON.stringify({
            type: 'action',
            action: { type: 'incPunt', team: 'home' }
        }))

        // Tutti i display devono aver ricevuto il broadcast
        for (const display of displays) {
            expect(display.send).toHaveBeenCalled()
            const msg = JSON.parse(display.send.mock.calls[display.send.mock.calls.length - 1][0])
            expect(msg.type).toBe('state')
            expect(punt(msg.state).home).toBe(1)
        }
    })

    it('dovrebbe gestire 100 azioni rapide in sequenza con stato finale corretto', () => {
        const controller = connectAndRegister(wss, 'controller')

        // 60 punti home, 40 punti guest
        for (let i = 0; i < 60; i++) {
            controller.emit('message', JSON.stringify({
                type: 'action',
                action: { type: 'incPunt', team: 'home' }
            }))
        }
        for (let i = 0; i < 40; i++) {
            controller.emit('message', JSON.stringify({
                type: 'action',
                action: { type: 'incPunt', team: 'guest' }
            }))
        }

        // Lo stato finale dipende da checkVittoria che blocca a 25+2
        // Home arriva a 25-0 → vittoria → blocca. Quindi punti home = 25
        const state = handler.getState()
        expect(punt(state).home).toBe(25)
        // Guest: non può segnare dopo vittoria? No, checkVittoria blocca solo il team che ha vinto?
        // Controlliamo: checkVittoria controlla ENTRAMBI i team.
        // A 25-0 → vittoria=true → incPunt per guest è anche bloccato
        expect(punt(state).guest).toBe(0)
    })

    it('dovrebbe garantire che tutti i display ricevano ogni update sotto carico', () => {
        const displays = []
        for (let i = 0; i < 10; i++) {
            displays.push(connectAndRegister(wss, 'display'))
        }

        const controller = connectAndRegister(wss, 'controller')

        // 5 azioni rapide
        for (let i = 0; i < 5; i++) {
            controller.emit('message', JSON.stringify({
                type: 'action',
                action: { type: 'incPunt', team: 'home' }
            }))
        }

        // Ogni display deve aver ricevuto esattamente 5 broadcast
        for (const display of displays) {
            expect(display.send).toHaveBeenCalledTimes(5)
        }

        // Verifica stato finale su tutti i display
        for (const display of displays) {
            const lastMsg = JSON.parse(display.send.mock.calls[4][0])
            expect(punt(lastMsg.state).home).toBe(5)
        }
    })
})
