import { defineStore } from "pinia";
import router from "@/router";
import { usePlayerStore } from "./player";
import { v4 as uuid } from "uuid";
import { useBotStore } from "./bot";
import { useApplicationStore } from '@/stores/application';
import audioPlayer from '@/classes/audioPlayer'
import { useWebsocketStore } from "./websocket";
import boardConstructor from '@/classes/boardConstructor'

export const useGameStore = defineStore("game", {
    state: () => ({
        id: null,
        mode: null,
        status: null,
        winner: null,
        owner: null,
        createdAt: null,
        boardSize: 10,
        currentMove: null,
        turn: {
            player: null,
            opponent: null,
        },
        player: {
            id: null,
            status: null,
            items: {},
        },
        opponent: {
            id: null,
            status: null,
            items: {}
        },
        moves: [],
    }),
    getters: {
        getId(state) {
            if (state.id == null) router.replace({ name: "idnex" });
            return state.id;
        },
    },
    actions: {
        // Get items keys for generation
        getAvailableItems() {
            return [...Array(23).keys()].slice(1)
        },
        // Create new game
        create(mode) { 
            const player = usePlayerStore();
            this.id = uuid();
            this.mode = mode;
            this.status = "new";
            this.owner = usePlayerStore().id;
            this.createdAt = new Date();
            this.player = {
                id: player.id,
                status: 'lobby',
                items: []
            }
            this.moves = [];
            router.replace({ name: "lobby", params: { gameId: this.id } });
        },
        // Send sync to connected player
        connected(playerId) {
            if (playerId != this.opponent.id) return
            useWebsocketStore().send({
                action:'sync',
                from: this.player.id,
                to: playerId,
                game: this.gameData()
            })
        },
        // Recieve join request and send welcome or decline
        join(opponentId) {
            const player = usePlayerStore()
            if (this.opponent.id != null && this.opponent.id != opponentId) {
                useWebsocketStore().send({
                    action:'decline',
                    from: player.id,
                    to: opponentId,
                    reason: '1'
                })
                return
            }
            this.opponent = {
                id: opponentId,
                status: "lobby",
                items: {}
            };
            useWebsocketStore().send({
                action:'welcome',
                from: player.id,
                to: opponentId,
                game: this.gameData()
            })
        },
        // Recieve welcome request and sync data
        welcome(game) {
            const player = usePlayerStore()
            this.player.id = player.id
            this.player.status = 'lobby'
            this.sync(game)
            router.replace({name: 'lobby', params: {gameId: this.id}})
        },
        // Recieve join decline message
        decline() {
            console.log('Owner send decline response')
        },
        // Recieve sync data
        sync(game) {
            this.id = game.id
            this.boardSize = game.boardSize
            this.createdAt = game.createdAt
            this.mode = game.mode,
            this.moves = game.moves,
            this.owner = game.owner,
            this.status = game.status,
            this.turn = game.turn,
            this.winner = game.winner,
            this.opponent = game.opponent
        },
        // Send player ready message
        ready() {
            const board = new boardConstructor(this.boardSize, this.getAvailableItems())
            if (!board.isAllItemsReady(this.player.items)) return
            if (this.player.status == "ready") {
                this.player.status = "lobby"
            } else {
                this.player.status = "ready"
            }
            if (this.mode == "single") {
                useBotStore().generate()
                return
            }
            useWebsocketStore().send({
                action:'sync',
                from: this.player.id,
                to: this.opponent.id,
                game: this.gameData()
            })
        },
        start() {
            if (this.player.status != "ready" || this.opponent.status != "ready") return
            if (this.owner == this.player.id) {
                this.status = "played";
                this.turn = {
                    player: this.player.id,
                    opponent: this.opponent.id
                }
                if (this.mode != "single") {
                    useWebsocketStore().send({
                        action:'sync',
                        from: this.player.id,
                        to: this.opponent.id,
                        game: this.gameData()
                    })
                }
            }
            router.replace({ name: "game", params: { gameId: this.id } });
        },
        async makeMove(targetCell) {
            let move = undefined
            console.log('%c Player move '+ this.player.id +' cell '+ targetCell + ' [gameStore]', "color:#e2a520;")
            if (this.mode == "single") {
                const bot = useBotStore()
                move = await bot.checkMove(targetCell);
                return move;
            }
            useWebsocketStore().send({
                action:'checkMove',
                from: this.player.id,
                to: this.opponent.id,
                game: this.gameData(),
                targetCell: targetCell
            })
            return            
        },
        async checkMove(targetCell) {
            console.log('%c Check move from '+ this.opponent.id +' cell '+ targetCell + ' [gameStore]', "color:#2dc9ac;")
            const application = useApplicationStore()
            const audio = new audioPlayer(application.settings.sound)
            let move = {};
            let targetItem = Object.values(this.player.items).filter((item) => item.cells.includes(targetCell))[0];
            move.targetCell = targetCell
            move.player = this.turn.player
            move.opponent = this.turn.opponent
            move.create = new Date()
            move.type = "move"
            if (targetItem != undefined) {
                audio.game('fish')
                move.item = {
                    id: targetItem.id,
                    type: targetItem.type,
                    name: targetItem.name,
                };
                let moves = this.getSuccessPlayerMoves(this.opponent.id)
                let itemsSize = this.getCompleteItemsSize(moves)
                if (this.isGameComplete(this.player.items, itemsSize)) {
                    this.winner = this.opponent.id
                }
            } else {
                audio.game('move')
                move.item = {
                    id: 'empty',
                    type: 'empty',
                    name: 'empty',
                };
            }
            console.log('%c Check move complete from '+ this.opponent.id + ' [gameStore]', 'color:#2dc9ac;', move)
            if (this.mode == 'single') return move;
            let message = {
                action:'completeMove',
                from: move.opponent.id,
                to: move.player.id,
                game: this.gameData(),
                move: move
            };
            useWebsocketStore().send(message)
            return
        },
        async completeMove(move) {
            console.log('%c Complete move START from ' + move.player + ' [gameStore]', 'color:#2dc9ac;',move)
            this.moves.push(move);
            const application = useApplicationStore()
            const audio = new audioPlayer(application.settings.sound)
            switch (move.item.type) {
                case "empty":
                    console.log('%c Set player turn ' + move.opponent + ' [gameStore]', 'color: #e2202a')
                    await audio.gameAsync('move')
                    this.turn.opponent = move.player
                    this.turn.player = move.opponent
                    break;
                case "fish":
                    await audio.gameAsync('fish')
                    let moves = this.getSuccessPlayerMoves(move.player)
                    let itemsSize = this.getCompleteItemsSize(moves)
                    if (itemsSize[move.item.id] == undefined) return
                    let items = this.opponent.items
                    if (move.player == 'bot' || move.player == this.opponent.id) items = this.player.items
                    if (itemsSize[move.item.id] == items[move.item.id].size) {
                        console.log('%c Player ' + move.player + ' complete item ' + move.item.id + ' [gameStore]', 'color:#2dc9ac;')
                    }
                    if (this.isGameComplete(items, itemsSize)) {
                        console.log('%c Game complete! Winner ' + move.player + ' [gameStore]', 'color:#2dc9ac;')
                        this.winner = move.player
                        useWebsocketStore().send({
                            action:'sync',
                            from: this.player.id,
                            to: this.opponent.id,
                            game: this.gameData()
                        })
                    }
                    break;
            }
            console.log('%c Complete move END from ' + move.player + ' [gameStore]', 'color:#2dc9ac;',move)
        },
        async sleep(ms) {
            console.log('%c SLEEP...  ' + ms + 'ms' + ' [gameStore]', 'color:#e2bb20;')
            return new Promise(resolve => setTimeout(resolve, ms));
        },
        getSuccessPlayerMoves(playerId) {
            return this.moves.filter((playerMove) => playerMove.player == playerId && playerMove.item.id != 'empty')
        },
        getCompleteItemsSize(moves) {
            let items = {}
            moves.forEach((move) => {
                if (items[move.item.id] == undefined) items[move.item.id] = 0
                items[move.item.id]++
            })
            return items
        },
        isGameComplete(items, completeItems) {
            let gameComplete = true
            Object.keys(items).forEach((itemId) => {
                if (completeItems[itemId] == undefined) gameComplete = false
                if (completeItems[itemId] != items[itemId].size) gameComplete = false
            })
            return gameComplete
        },
        gameData() {
            let data = {
                id: this.id,
                boardSize: this.boardSize,
                createdAt: this.createdAt,
                mode: this.mode,
                moves: this.moves,
                owner: this.owner,
                status: this.status,
                turn: this.turn,
                winner: this.winner,
                opponent: {
                    id: this.player.id,
                    items: {},
                    status: this.player.status
                }
            };
            Object.values(this.player.items).forEach((item) => {
                data.opponent.items[item.id] = { size: item.size, margin: item.margin };
            });
            return data
        }
    },
    persist: true,
});