import BiscaGame from "../classes/BiscaGame.js"; import axios from "axios"; const API_URL = "http://localhost:8000/api/v1"; class BiscaMatch { constructor(apiData, emitStateCallback, token) { this.id = apiData.id; this.emitStateCallback = emitStateCallback; this.token = token; this.stake = apiData.stake; this.type = apiData.type; this.status = apiData.status; this.player1Id = apiData.player1_user_id; this.player2Id = apiData.player2_user_id; this.marks = { [this.player1Id]: apiData.player1_marks || 0, [this.player2Id]: apiData.player2_marks || 0, }; this.points = { [this.player1Id]: apiData.player1_points || 0, [this.player2Id]: apiData.player2_points || 0, }; this.currentGame = null; this.GHOST_ID = 1; if (this.status === "Playing" && this.player2Id !== this.GHOST_ID) { this.startNewGame(); } } async joinPlayer(userId) { if (this.player2Id !== this.GHOST_ID) return false; if (userId === this.player1Id) return false; this.player2Id = userId; this.marks[userId] = 0; this.points[userId] = 0; this.status = "Playing"; await this.startNewGame(); return true; } getAuthHeaders() { return { headers: { Authorization: this.token }, }; } async startNewGame() { console.log(`[Match ${this.id}] A iniciar nova mão (Game DB)...`); let newGameDbId = null; try { let payloadP1 = this.player1Id; let payloadP2 = this.player2Id; if (this.player2Id !== this.GHOST_ID) { console.log( "🔄 Multiplayer detetado: Trocando ordem para satisfazer a API..." ); payloadP1 = this.player2Id; payloadP2 = this.player1Id; } const payload = { type: this.type, player1_user_id: payloadP1, player2_user_id: payloadP2, match_id: this.id, status: "Playing", began_at: new Date().toISOString(), }; console.log("📤 [Payload Adaptado]:", JSON.stringify(payload, null, 2)); const res = await axios.post( `${API_URL}/games`, payload, this.getAuthHeaders() ); const gameData = res.data.game || res.data.data || res.data; newGameDbId = gameData.id; console.log(`[Match ${this.id}] Game DB criado: #${newGameDbId}`); const apiP1 = gameData.player1_user_id; const apiP2 = gameData.player2_user_id; const p1 = { id: apiP1, name: `User ${apiP1}` }; const p2 = { id: apiP2, name: `User ${apiP2}` }; this.currentGame = new BiscaGame( this.id, this.type, p1, p2, newGameDbId, (result) => { this.handleGameEnd(result); } ); this.currentGame.init(); if (this.emitStateCallback) { this.emitStateCallback("match:new-round", this.getState()); } } catch (e) { if (e.response) { console.error( `❌ Erro API (${e.response.status}):`, JSON.stringify(e.response.data) ); } else { console.error(`❌ Erro Código: ${e.message}`); } } } async handleGameEnd(result) { console.log( `[Match ${this.id}] Mão terminada. Vencedor: ${result.winnerId}` ); this.points[result.player1_id] += result.player1_points; this.points[result.player2_id] += result.player2_points; if (this.currentGame && this.currentGame.dbId) { try { const payload = { status: "Ended", winner_user_id: result.winnerId, loser_user_id: result.loserId, is_draw: result.isDraw, player1_points: result.player1_points, player2_points: result.player2_points, ended_at: new Date().toISOString(), }; console.log( `📤 A enviar update para Game #${this.currentGame.dbId}...` ); // --- CORREÇÃO IMPORTANTE: Forçar o header aqui --- const config = { headers: { Authorization: this.token, "Content-Type": "application/json", Accept: "application/json", }, }; await axios.put( `${API_URL}/games/${this.currentGame.dbId}`, payload, config ); console.log( `✅ Game #${this.currentGame.dbId} atualizado com sucesso.` ); } catch (e) { // --- LOG DETALHADO PARA VER O MOTIVO DO 403 --- if (e.response) { console.error(`❌ ERRO CRÍTICO API (${e.response.status}):`); // Isto vai imprimir o JSON exato que o Laravel devolve console.error(JSON.stringify(e.response.data, null, 2)); } else { console.error("Erro Axios:", e.message); } } } else { console.warn("⚠️ Ignorado: Não há ID de jogo na DB para atualizar."); } // ... (resto da lógica de atribuir marcas e finishMatch mantém-se igual) let marksToAdd = 0; let roundWinnerId = result.winnerId; if (roundWinnerId) { const winningScore = roundWinnerId == result.player1_id ? result.player1_points : result.player2_points; if (winningScore === 120) marksToAdd = 4; else if (winningScore > 90) marksToAdd = 2; else if (winningScore >= 60) marksToAdd = 1; this.marks[roundWinnerId] += marksToAdd; } if (this.marks[this.player1Id] >= 4 || this.marks[this.player2Id] >= 4) { await this.finishMatch(); if (this.emitStateCallback) { this.emitStateCallback("match:ended", this.getState()); } } else { console.log(`[Match ${this.id}] Ninguém ganhou 4 marcas. A baralhar...`); await this.startNewGame(); } } async finishMatch() { this.status = "Ended"; const winnerId = this.marks[this.player1Id] >= 4 ? this.player1Id : this.player2Id; const loserId = winnerId == this.player1Id ? this.player2Id : this.player1Id; try { await axios.put( `${API_URL}/matches/${this.id}`, { status: "Ended", winner_user_id: winnerId, loser_user_id: loserId, player1_marks: this.marks[this.player1Id], player2_marks: this.marks[this.player2Id], player1_points: this.points[this.player1Id], player2_points: this.points[this.player2Id], }, this.getAuthHeaders() ); console.log(`[Match ${this.id}] Encerrado com sucesso na API.`); } catch (e) { console.error(`Erro ao fechar Match API: ${e.message}`); } } playCard(userId, cardId) { if (!this.currentGame || this.status !== "Playing") return; return this.currentGame.playCard(userId, cardId); } getState() { let gameState = null; if (this.currentGame) { const p1Id = this.player1Id; const p2Id = this.player2Id; if (this.currentGame.players && this.currentGame.players[p1Id]) { gameState = this.currentGame.getStateForPlayer(p1Id); } else if (this.currentGame.players && this.currentGame.players[p2Id]) { gameState = this.currentGame.getStateForPlayer(p2Id); } else if (this.currentGame.players) { const availableIds = Object.keys(this.currentGame.players); if (availableIds.length > 0) { console.warn( `⚠️ [Match ${this.id}] IDs desalinhados! A usar vista do user ${availableIds[0]}` ); gameState = this.currentGame.getStateForPlayer(availableIds[0]); } } } return { matchId: this.id, status: this.status, marks: this.marks, points: this.points, game: gameState, }; } } export default BiscaMatch;