import pg from 'pg';
import fs from 'fs';
import { convertMsgType, logError } from './utils.js';
import { exec } from 'child_process';

const { Client } = pg;

const getClient = async () => {
    const client = new Client({
        user: process.env.POSTGRES_USER,
        password: process.env.POSTGRES_PASSWORD,
        host: process.env.POSTGRES_HOST,
        port: process.env.POSTGRES_PORT,
        database: process.env.POSTGRES_DB
    });
    await client.connect();

    await client.query(fs.readFileSync('./scheme.psql').toString());

    if ((await client.query("SELECT * FROM cities")).rowCount < 6079) {
        await client.query("DELETE FROM cities");
        
        //Not sure if pg has support for such kind of things, sooooooooo 
        exec(`psql -h ${process.env.POSTGRES_HOST} -p ${process.env.POSTGRES_PORT} -d ${process.env.POSTGRES_DB} -U ${process.env.POSTGRES_USER} -f ./cities.sql`, (error) => {
            if (error) logError(error);
        })
    }

    return client;
};

export const db = await getClient();


const getAmountOfUserPictures = async (roomId) => {
    return (await db.query("SELECT COUNT(*) FROM media WHERE owner = $1 AND purpose = 'p'", [roomId])).rows[0].count;
};

const getCurrentUserAction = async (roomId) => {
    return (await db.query('SELECT current_action FROM users WHERE room_id = $1', [roomId])).rows[0].current_action;
};

const setUserState = async (roomId, state) => {
    await db.query("UPDATE users SET current_action = $1 WHERE room_id = $2", [state, roomId]);
};

const appendUserPictures = async (roomId, mxc, type) => {
    await db.query("INSERT INTO media (owner, type, purpose, url) VALUES ($1, $2, 'p', $3)", [roomId, type, mxc]);
};

const eraseUser = async (roomId) => {
    await db.query("DELETE FROM users WHERE room_id = $1", [roomId]);
};

const eraseUserLikes = async (roomId) => {
    await db.query("DELETE FROM likes WHERE sender = $1 OR recipient = $1", [roomId]);
};

const eraseUserMedia = async (roomId) => {
    await db.query("DELETE FROM media WHERE owner = $1", [roomId]);
};

const selectProfilesForUser = async (roomId) => {
    let myInterest = (await db.query("SELECT interest FROM users WHERE room_id = $1", [roomId])).rows[0].interest;
    let mySex = (await db.query("SELECT sex FROM users WHERE room_id = $1", [roomId])).rows[0].sex;
    let userAge = (await db.query("SELECT age FROM users WHERE room_id = $1", [roomId])).rows[0].age;
    //Selecting profiles other than user's and with difference in age +-2.
    let user;
    if (myInterest === 'b') // both, no matter what sex
        user = (await db.query(`SELECT
                                    room_id, name, age, sex, description, country, city FROM users
                                WHERE
                                    age::numeric <@ ANY(ARRAY[numrange($1 - 2, $1 + 2)])
                                AND room_id != $2
                                AND (interest = $3 OR interest = 'b')
                                ORDER BY RANDOM()
                                LIMIT 1`, [userAge, roomId, mySex])
        ).rows[0];
    else {
        user = (await db.query(`SELECT 
                                    room_id, name, age, sex, description, country, city FROM users
                                WHERE
                                    age::numeric <@ ANY(ARRAY[numrange($1 - 2, $1 + 2)])
                                AND room_id != $2
                                AND sex = $3
                                AND (interest = $4 OR interest = 'b')
                                ORDER BY RANDOM()
                                LIMIT 1`, [userAge, roomId, myInterest, mySex])
        ).rows[0];
    }
    if (!user) return null;
    let media = await getUserProfilePictures(user.room_id);

    user.media = media;
    return user;
};

const setUserCurrentlyViewingProfile = async (roomId, anotherRoomId) => {
    await db.query("UPDATE users SET currently_viewing = $1 WHERE room_id = $2", [anotherRoomId, roomId]);
};

const getUserCurrentlyViewingProfile = async (roomId) => {
    return (await db.query("SELECT currently_viewing FROM users WHERE room_id = $1", [roomId])).rows[0].currently_viewing;
};

//Newlike is a room id of a user who was liked_profiles
const appendUserLikes = async (roomId, newLike) => {
    //We need to delete likes that was read from both, so we can add a new (the same) like.
    await db.query(`DELETE FROM likes l1
                    USING likes l2
                    WHERE l1.sender = l2.recipient
                    AND l1.recipient = l2.sender
                    AND l1.read = TRUE
                    AND l2.read = TRUE`);

    await db.query("INSERT INTO likes (sender, recipient) VALUES ($1, $2) ON CONFLICT DO NOTHING", [roomId, newLike]);
};

const getUserLikes = async (roomId) => {
    return (await db.query("SELECT recipient FROM likes WHERE sender = $1", [roomId])).rows[0].recipient;
};

const checkForMutualLike = async (roomId1, roomId2) => {
    return (await db.query(`SELECT COUNT(*) > 0 AS value
                           FROM likes l1
                           JOIN likes l2 ON l1.sender = l2.recipient AND l1.recipient = l2.sender
                           WHERE (l1.read = FALSE OR l2.read = FALSE)
                           AND l1.sender = $1
                           AND l1.recipient = $2`, [roomId1, roomId2])).rows[0].value;
};

const getProfileInfo = async (roomId) => {
    let user = (await db.query("SELECT mx_id, room_id, name, age, sex, description, country, city FROM users WHERE room_id = $1", [roomId])).rows[0];
    if (!user) return null;
    let media = await getUserProfilePictures(user.room_id);
    user.media = media;
    return user;
};

const getUserProfilePictures = async (roomId) => {
    return (await db.query("SELECT url, type FROM media WHERE owner = $1 AND purpose = 'p'", [roomId])).rows;
};

const getAllLikesForUser = async (roomId) => {
    return (await db.query("SELECT sender FROM likes WHERE recipient = $1 AND read = FALSE", [roomId])).rows;
};

const markLikeAsRead = async (roomId, recipient,) => {
    await db.query("UPDATE likes SET read = TRUE WHERE sender = $1 AND recipient = $2", [roomId, recipient]);
};

const uploadMediaAsMessage = async (roomId, type, mxc) => {
    await db.query("INSERT INTO media(owner, type, purpose, url) VALUES ($1, $2, 'm', $3)", [roomId, convertMsgType(type), mxc]);
};

const insertMessageIntoDB = async (roomId, recipient, type, content) => {
    let rowAmount = (await db.query("INSERT INTO messages (sender, recipient, type, content) VALUES ($1, $2, $3, $4) ON CONFLICT(sender, recipient) DO NOTHING RETURNING *", [roomId, recipient, convertMsgType(type), content])).rowCount;
    return rowAmount == 1;
};

const getUnreadMessages = async (roomId) => {
    return (await db.query("SELECT (SELECT mx_id FROM users WHERE room_id = sender LIMIT 1), sender, type, content FROM messages WHERE recipient = $1 AND read = FALSE", [roomId])).rows
};

const markMessageAsRead = async (roomId, recipient) => {
    return (await db.query("UPDATE messages SET read = TRUE WHERE sender = $1 AND recipient = $2", [roomId, recipient]));
}

const setUserLanguage = async (roomId, language) => {
    await db.query("UPDATE users SET language = $1 WHERE room_id = $2", [language, roomId]);
}

const getUserLanguage = async (roomId) => {
    return (await db.query("SELECT language FROM users WHERE room_id = $1", [roomId])).rows[0].language;
}

export {
    eraseUser,
    appendUserPictures,
    setUserState,
    getCurrentUserAction,
    getAmountOfUserPictures,
    selectProfilesForUser,
    setUserCurrentlyViewingProfile,
    getUserCurrentlyViewingProfile,
    appendUserLikes,
    getUserLikes,
    checkForMutualLike,
    getProfileInfo,
    getAllLikesForUser,
    markLikeAsRead,
    eraseUserLikes,
    eraseUserMedia,
    uploadMediaAsMessage,
    insertMessageIntoDB,
    getUnreadMessages,
    markMessageAsRead,
    setUserLanguage,
    getUserLanguage
};