import { initDb } from "./db.js";
import { getToken } from './account.js';

let dat = {};

let cooldown = {};
let lastTurn = '';

let users = {};

let initTile = async (x, y, username) => {
    let db = await initDb();

    let data = new Array(25);

    let blockData = await db.all('SELECT * FROM block WHERE x = ? AND y = ?', [
        x,
        y
    ]);

    let occupied = await db.all('SELECT * FROM tile WHERE username = ?', [
        username
    ]);

    let blockEntry = blockData[0];

    if (!dat[`${x},${y}`]) {
        dat[`${x},${y}`] = {};
    }

    dat[`${x},${y}`].type = blockEntry ? (blockEntry.status == 1 ? 'select' : 'claimed') : ''

    if (username && blockEntry && blockEntry.status == 1 && (!occupied || occupied.length < 1)) {
        for (let i = 0; i < 25; i++) {
            data[i] = username;
        }

        let vals = [
            [x + 1, y],
            [x, y + 1],
            [x - 1, y],
            [x, y - 1]
        ];

        await db.run('UPDATE block SET status = 0 WHERE x = ? AND y = ?', [
            x,
            y
        ]);

        for (let thing of vals) {
            await db.run('INSERT INTO block (status, x, y) VALUES (1,?,?)', [
                thing[0],
                thing[1]
            ]);
        }


    } else if (!blockEntry || blockEntry.status == 1) {
        return false;
    } else {
        for (let i = 0; i < 25; i++) {
            data[i] = data[i] || '!nobody';
        }
    }

    let table = await db.all('SELECT * FROM tile WHERE x < ? AND y < ? AND x > ? AND y > ?', [
        x * 5 + 5,
        y * 5 + 5,
        x * 5 - 1,
        y * 5 - 1,
    ]);

    for (let i in data) {
        let x2 = i % 5 + x * 5;
        let y2 = Math.floor(i / 5) + y * 5;

        let tile = table.find(({ x, y }) => x == x2 && y == y2);


        if (tile) {
            data[i] = tile.username;
        } else {
            await db.run('DELETE FROM tile WHERE x = ? AND y = ?', [
                x2,
                y2
            ]);
            await db.run('INSERT INTO tile (username, x, y) VALUES (?, ?, ?)', [
                data[i],
                x2,
                y2
            ]);
        }

    }

    dat[`${x},${y}`].data = data;

    return dat;
}


(async () => {

    let db = await initDb();

    let blocks = await db.all('SELECT * FROM block');

    for (let block of blocks) {
        initTile(block.x, block.y);
    }

    let dataUser = await db.all('SELECT * FROM player');

    for (let entry of dataUser) {
        dat[`_user_${entry.username}`] = entry;
    }
})();

let socketHandler = async (socket, io) => {
    let user;

    let db = await initDb();

    socket.on('join', async ({ token }) => {
        user = await getToken(token);

        if (user == '!nobody') {
            user = '';
            socket.emit('data','noauth');
        }

        users[user] = true;

        socket.emit('data', dat);

        socket.emit('cooldown', cooldown[user]);
        socket.emit('turn',lastTurn);

        let dataUser = await db.all('SELECT * FROM player WHERE username = ?', [
            user
        ]);

        if (dataUser && dataUser.length > 0) {
            dat[`_user_${user}`] = dataUser[0];
        } else {
            dat[`_user_${user}`] = {
                'username': user,
                'score': 0,
                'out': 0,
                'color': `hsl(${Math.random() * 360}deg,${Math.random() * 30 + 65}%,${Math.random() * 30 + 60}%)`
            };

            await db.run('INSERT INTO player (username, score, out, color) VALUES (?,?,?,?)', [
                dat[`_user_${user}`].username,
                dat[`_user_${user}`].score,
                dat[`_user_${user}`].out,
                dat[`_user_${user}`].color
            ]);
        }

    })

    socket.on('color', async (color) => {

        await db.run('UPDATE player SET color = ? WHERE username = ?', [
            color,
            user
        ]);

        dat[`_user_${user}`].color = color;

        socket.emit('data', dat);
    })

    socket.on('disconnect', () => {
        delete users[user];
    })

    socket.on('claim', async ({ x, y }) => {
        if (!user) return;

        x = x * 1;
        y = y * 1;

        await initTile(x, y, user);

        let blocks = await db.all('SELECT * FROM block');

        for (let block of blocks) {
            await initTile(block.x, block.y);
        }

        io.emit('data', dat);
    })

    socket.on('conquer', async ({ x, y }) => {
        if (!user) return;

        x = x * 1;
        y = y * 1;

        let x2 = Math.floor(x / 5);
        let y2 = Math.floor(y / 5);

        let x3 = x % 5;
        let y3 = Math.floor(y / 5);

        let key = `${x2},${y2}`

        if (!dat[key]) return;

        let vals = [
            [x + 1, y],
            [x, y + 1],
            [x - 1, y],
            [x, y - 1]
        ];

        let canClaim = false;

        for (let val of vals) {
            let isClaimed = await db.all('SELECT * FROM tile WHERE x = ? AND y = ?', [
                val[0],
                val[1]
            ]);

            console.log(isClaimed)

            if (isClaimed && isClaimed.length > 0 && isClaimed[0].username == user) {
                canClaim = true;
                break;
            }
        }

        if (!canClaim) return;

        if (new Date() * 1 < cooldown[user] || lastTurn == user) return;

        let userFactor = Math.min(Object.keys(users).length, 10);
        userFactor = Math.max(userFactor, 1);

        cooldown[user] = (60000 / userFactor) + (new Date() * 1);
        lastTurn = user;

        socket.emit('cooldown', cooldown[user]);
        io.emit('turn',lastTurn);

        let formerOwner = await db.all('SELECT * FROM tile WHERE x = ? AND y = ?', [
            x,
            y
        ]);

        let formerOwnerStr = formerOwner[0].username;

        formerOwner = formerOwner[0] ? `_user_${formerOwner[0].username}` : false;
        
        await db.run('UPDATE tile SET username = ? WHERE x = ? AND y = ?', [
            user,
            x,
            y
        ]);

        dat[key].data[`${x3},${y3}`] = user;

        await initTile(x2, y2);

        io.emit('data', dat);

        if (!dat[formerOwner]) {
            dat[formerOwner] = {
                'username': formerOwnerStr,
                'score': 0,
                'out': 0,
                'color': `hsl(${Math.random() * 360}deg,${Math.random() * 30 + 65}%,${Math.random() * 30 + 60}%)`
            };

            await db.run('INSERT INTO player (username, score, out, color) VALUES (?,?,?,?)', [
                dat[formerOwner].username,
                dat[formerOwner].score,
                dat[formerOwner].out,
                dat[formerOwner].color
            ]);
        }

        let tCount = (await db.all('SELECT * FROM tile WHERE username = ?', [
            formerOwnerStr
        ])).length;

        if (tCount < 26) {
            dat[formerOwner].score--;
        } else {
            dat[formerOwner].score--;
            dat[`_user_${user}`].score++;
        }

        await db.run('UPDATE player SET score = ? WHERE username = ?', [
            dat[`_user_${user}`].score,
            user
        ]);

        await db.run('UPDATE player SET score = ? WHERE username = ?', [
            dat[formerOwner].score,
            formerOwnerStr
        ]);

        console.log(dat[`_user_${user}`].score,dat[formerOwner].score);
    });
}

export {
    socketHandler
};