import {
    EncryptionAlgorithm,
    MatrixClient,
    MessageEvent,
    RustSdkCryptoStorageProvider,
    SimpleFsStorageProvider,
} from "matrix-bot-sdk";

import { StoreType } from "@matrix-org/matrix-sdk-crypto-nodejs";

import {
    convertMsgType,
    logError,
    logInfo,
    readConfig,
    uploadMediaFromEvent
} from './utils.js';

import {
    appendUserLikes,
    appendUserPictures,
    checkForMutualLike,
    fullEraseUser,
    getAmountOfUserPictures,
    getCurrentUserAction,
    getUnreadMessages,
    getUserCurrentlyViewingProfile,
    insertMessageIntoDB,
    markMessageAsRead,
    setUserState,
    uploadMediaAsMessage,
    setUserLanguage,
    getUserLanguage,
    findCity,
    doesUserExist,
    createUser,
    eraseUserPfp,
    getProfileInfo
} from './db.js';

import {
    processRequest,
    showRandomProfileToUser,
    showNewLikes,
    wait_start,
    range,
    interest,
    pictures,
    loc,
    age,
    sex,
    showProfileToUser,
    showProfile
} from "./interactions.js";

import { db } from "./db.js";

import fs from "fs";

import { I18n } from "i18n-js";
const i18n = new I18n({
    en: JSON.parse(fs.readFileSync("./translations/en.json")),
    ru: JSON.parse(fs.readFileSync("./translations/ru.json"))
});
i18n.defaultLocale = "en";

const config = readConfig();

const homeserverUrl = config.homeserverURL;
const accessToken = config.token;
const maxAmountOfPhotoesPerUser = config.maxAmountOfPhotoesPerUser;

const crypto = new RustSdkCryptoStorageProvider("./bot_data/encryption_bot_sled", StoreType.Sled);
const storage = new SimpleFsStorageProvider("./bot_data/bot.json");

const client = new MatrixClient(homeserverUrl, accessToken, storage, crypto);

client.on("room.message", async (roomId, event) => {
    try {
        if (event.sender === await client.getUserId()) return;

        if (!await doesUserExist(roomId)) {
            i18n.locale = 'en'
            await client.sendText(roomId, i18n.t(["errors", "usernotexists"]));
            i18n.locale = 'ru'
            await client.sendText(roomId, i18n.t(["errors", "usernotexists"]));
            let mx_id = event.sender;
            await createUser(mx_id, roomId);

            return;
        }

        if (event.content?.msgtype !== 'm.text' && event.content?.msgtype !== 'm.image' && event.content?.msgtype !== 'm.video') return;

        let current_action = await getCurrentUserAction(roomId);
        let answer = event.content.body;
        let msgtype = event.content.msgtype

        let preferredLanguage = await getUserLanguage(roomId);
        if (!preferredLanguage) preferredLanguage = i18n.defaultLocale;
        i18n.locale = preferredLanguage;

        switch (current_action) {
            case "wait_start":
                await wait_start(client, roomId, current_action, answer, false);
                break;
            case "wait_start_u":
                await wait_start(client, roomId, current_action, answer, true);
                break;

            case "location":
                await loc(client, roomId, current_action, answer, false);
                break;
            case "location_u":
                await loc(client, roomId, current_action, answer, true);
                break;

            case "range":
                await range(client, roomId, current_action, answer, false);
                break;
            case "range_u":
                await range(client, roomId, current_action, answer, true);
                break;

            case "name":
                await processRequest(client, roomId, current_action, answer, 'age');
                break;
            case "name_u":
                await processRequest(client, roomId, current_action, answer, 'menu');
                break;

            case "age":
                await age(client, roomId, current_action, answer, false);
                break;
            case "age_u":
                await age(client, roomId, current_action, answer, true);
                break;

            case "sex":
                await sex(client, roomId, current_action, answer, false);
                break;
            case "sex_u":
                await sex(client, roomId, current_action, answer, true);
                break;

            case "interest":
                await interest(client, roomId, current_action, answer, false);
                break;
            case "interest_u":
                await interest(client, roomId, current_action, answer, true);
                break;

            case "description":
                await processRequest(client, roomId, current_action, answer, 'pictures');
                break;
            case "description_u":
                await processRequest(client, roomId, current_action, answer, 'menu');
                break;

            case "pictures":
                pictures(client, roomId, current_action, event, false);
                break;
            case "pictures_u":
                pictures(client, roomId, current_action, event, true);
                break;

            case "language_u":
                if (answer !== "ru" && answer !== "en") return;
                await setUserLanguage(roomId, answer);
                i18n.locale = answer;
                await setUserState(roomId, "menu");
                await client.sendText(roomId, i18n.t(["general", "menu"]));
                break;

            case "view_profiles":
                if (answer == '👍️' || answer == '❤️' || answer == '1') {
                    let currently_viewing = await getUserCurrentlyViewingProfile(roomId);
                    await appendUserLikes(roomId, currently_viewing);
                    let value = await checkForMutualLike(roomId, currently_viewing);
                    if (value) {
                        await client.sendText(currently_viewing, i18n.t(["general", "newlikes"]));
                        await client.sendText(roomId, i18n.t(["general", "newlikes"]));
                    }
                } else if (answer == '💌' || answer == '3') {
                    await setUserState(roomId, 'send_message');
                    await client.sendText(roomId, i18n.t(["general", "message"]));
                    return;
                } else if (answer == '🏠️' || answer == '4') {
                    await client.sendText(roomId, i18n.t(["general", "menu"]));
                    await setUserState(roomId, 'menu');
                    return;
                }
                await showRandomProfileToUser(client, roomId);
                break;
            case 'send_message':
                let recipient = await getUserCurrentlyViewingProfile(roomId);
                let content;
                if (msgtype == "m.image" || msgtype == "m.video") {
                    let mxc = await uploadMediaFromEvent(client, event);
                    await uploadMediaAsMessage(roomId, msgtype, mxc);
                    content = mxc;
                } else {
                    content = answer;
                }
                if (await insertMessageIntoDB(roomId, recipient, msgtype, content)) {
                    await client.sendText(recipient, i18n.t(["general", "newmessage"]));
                    await client.sendText(roomId, i18n.t(["general", "messagesent"]));
                } else {
                    await client.sendText(roomId, i18n.t(["errors", "alreadymessaged"]));
                }
                await setUserState(roomId, 'view_profiles');
                await showRandomProfileToUser(client, roomId);
                break;
            case 'menu':
                switch (answer) {
                    case '0':
                        let profile = await getProfileInfo(roomId);
                        await showProfile(client, roomId, profile);
                        await client.sendText(roomId, i18n.t(["general", "menu"]));
                    break;
                    case '1':
                        await setUserState(roomId, 'view_profiles');
                        await showRandomProfileToUser(client, roomId);
                        break;
                    case '2':
                        await showNewLikes(client, roomId);
                        await client.sendText(roomId, i18n.t(["general", "menu"]));
                        break;
                    case '3':
                        let unreadMessages = await getUnreadMessages(roomId);
                        if (!unreadMessages || unreadMessages.length == 0) {
                            await client.sendText(roomId, i18n.t(["general", "nonewmessages"]));
                            return;
                        }
                        await client.sendText(roomId, i18n.t(["general", "msg"]));

                        for (let message of unreadMessages) {
                            await client.sendText(roomId, i18n.t(["general", "showmessage"], { user: message.mx_id }));

                            if (message.type == "t") {
                                await client.sendText(roomId, message.content)
                            } else if (message.type == "p" || message.type == "v") {
                                let msgtype;

                                if (message.type == "p") {
                                    msgtype = "m.image";
                                } else if (message.type == "v") {
                                    msgtype = "m.video";
                                }

                                await client.sendMessage(roomId, {
                                    msgtype: msgtype,
                                    body: "Message",
                                    url: message.content
                                });
                            }
                            await markMessageAsRead(message.sender, roomId);
                        }
                        await client.sendText(roomId, i18n.t(["general", "menu"]));
                        break;
                    case '4':
                        setUserState(roomId, 'location_u');
                        await client.sendText(roomId, i18n.t(["setup", "location"]));
                        break;
                    case '5':
                        setUserState(roomId, 'range_u');
                        await client.sendText(roomId, i18n.t(["setup", "range"]));
                        break;
                    case '6':
                        setUserState(roomId, 'name_u');
                        await client.sendText(roomId, i18n.t(["setup", "name"]));
                        break;
                    case '7':
                        setUserState(roomId, 'age_u');
                        await client.sendText(roomId, i18n.t(["setup", "age"]));
                        break;
                    case '8':
                        setUserState(roomId, 'sex_u');
                        await client.sendText(roomId, i18n.t(["setup", "sex"]));
                        break;
                    case '9':
                        setUserState(roomId, 'interest_u');
                        await client.sendText(roomId, i18n.t(["setup", "interest"]));
                        break;
                    case '10':
                        setUserState(roomId, 'description_u');
                        await client.sendText(roomId, i18n.t(["setup", "description"]));
                        break;
                    case '11':
                        await eraseUserPfp(roomId);
                        setUserState(roomId, 'pictures_u');
                        await client.sendText(roomId, i18n.t(["setup", "pictures"]));
                        break;
                    case '12':
                        setUserState(roomId, 'language_u');
                        await client.sendText(roomId, i18n.t(["setup", "language"]));
                    default:
                        await client.sendText(roomId, i18n.t(["errors", "didntunderstand"]));
                        await client.sendText(roomId, i18n.t(["general", "menu"]));
                        break;
                }
                break;
            default:
                await client.sendText(roomId, i18n.t(["errors", "didntunderstand"]));
                return;
        }
    } catch (e) {
        logError(e);
    }
});

client.on("room.event", async (roomId, event) => {
    try {
        if (event.type === "m.room.member" && event.content?.membership === "leave") {
            await fullEraseUser(roomId);
            logInfo(`Bot has left a room with ID  ${roomId}`);
            client.leaveRoom(roomId);
        }
    } catch (e) {
        logError(e);
    }
});

client.on("room.invite", async (roomId, event) => {
    try {
        await client.joinRoom(roomId);

        let members = await client.getAllRoomMembers(roomId)
        let isDM = members.length == 2 ? true : false;

        if (!isDM) {
            await client.sendText(roomId, i18n.t(["errors", "notadm"]));
            await client.leaveRoom(roomId);
        }

        logInfo(`Bot has joined a room with ID ${roomId}`);

        let mx_id = event.sender;

        if (!await doesUserExist(roomId)) {
            await createUser(mx_id, roomId);

            i18n.locale = "en";
            await client.sendText(roomId, i18n.t(["general", "welcome"]));
            i18n.locale = "ru";
            await client.sendText(roomId, i18n.t(["general", "welcome"]));
        } else {
            setUserState(roomId, 'view_profiles');
        }
    } catch (e) {
        logError(e);
    }

});

client.start().then(() => logInfo("Bot started!"));