package com.alttd.altitudeweb.setup;

import com.alttd.altitudeweb.database.Databases;
import com.alttd.altitudeweb.database.web_db.KeyPairMapper;
import com.alttd.altitudeweb.database.web_db.PrivilegedUserMapper;
import com.alttd.altitudeweb.database.web_db.SettingsMapper;
import com.alttd.altitudeweb.database.web_db.forms.AppealMapper;
import com.alttd.altitudeweb.database.web_db.forms.DiscordAppealMapper;
import com.alttd.altitudeweb.database.web_db.forms.StaffApplicationMapper;
import com.alttd.altitudeweb.database.web_db.mail.EmailVerificationMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSession;
import org.jetbrains.annotations.NotNull;

import java.sql.SQLException;
import java.sql.Statement;

@Slf4j
public class InitializeWebDb {

    protected static void init() {
        log.info("Initializing WebDb");
        Connection.getConnection(Databases.DEFAULT, (configuration) -> {
                      configuration.addMapper(SettingsMapper.class);
                      configuration.addMapper(KeyPairMapper.class);
                      configuration.addMapper(PrivilegedUserMapper.class);
                      configuration.addMapper(AppealMapper.class);
                      configuration.addMapper(DiscordAppealMapper.class);
                      configuration.addMapper(StaffApplicationMapper.class);
                      configuration.addMapper(EmailVerificationMapper.class);
                  }).join()
                  .runQuery(sqlSession -> {
                      createSettingsTable(sqlSession);
                      createKeyTable(sqlSession);
                      createPrivilegedUsersTable(sqlSession);
                      createPrivilegesTable(sqlSession);
                      createAppealTable(sqlSession);
                      createdDiscordAppealTable(sqlSession);
                      createStaffApplicationsTable(sqlSession);
                      createUserEmailsTable(sqlSession);
                  });
        log.debug("Initialized WebDb");
    }

    private static void createSettingsTable(@NotNull SqlSession sqlSession) {
        String query = """
                CREATE TABLE IF NOT EXISTS db_connection_settings
                (
                    internal_name     VARCHAR(255) NOT NULL,
                    name              VARCHAR(255) NOT NULL,
                    username          VARCHAR(255) NOT NULL,
                    password          VARCHAR(255) NOT NULL,
                    host              VARCHAR(255) NOT NULL,
                    port              INT NOT NULL,
                    CONSTRAINT pk_internal_name PRIMARY KEY (internal_name)
                );
                """;
        try (Statement statement = sqlSession.getConnection().createStatement()) {
            statement.execute(query);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    private static void createKeyTable(@NotNull SqlSession sqlSession) {
        String query = """
                CREATE TABLE IF NOT EXISTS key_pair (
                    id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
                    private_key TEXT NOT NULL,
                    public_key TEXT NOT NULL,
                    created_at TIMESTAMP NOT NULL
                );
                """;
        try (Statement statement = sqlSession.getConnection().createStatement()) {
            statement.execute(query);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    private static void createPrivilegedUsersTable(@NotNull SqlSession sqlSession) {
        String query = """
                CREATE TABLE IF NOT EXISTS privileged_users (
                    id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
                    uuid UUID UNIQUE NOT NULL
                );
                """;
        try (Statement statement = sqlSession.getConnection().createStatement()) {
            statement.execute(query);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    private static void createPrivilegesTable(@NotNull SqlSession sqlSession) {
        String query = """
                CREATE TABLE IF NOT EXISTS privileges (
                    id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
                    user_id int NOT NULL,
                    privileges VARCHAR(36) NOT NULL,
                    CONSTRAINT fk_privileges_user FOREIGN KEY (user_id)
                        REFERENCES privileged_users(id)
                        ON DELETE CASCADE
                        ON UPDATE CASCADE
                );
                """;
        try (Statement statement = sqlSession.getConnection().createStatement()) {
            statement.execute(query);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    private static void createUserEmailsTable(@NotNull SqlSession sqlSession) {
        String query = """
                CREATE TABLE IF NOT EXISTS user_emails (
                    id UUID NOT NULL DEFAULT (UUID()) PRIMARY KEY,
                    user_uuid UUID NOT NULL,
                    email VARCHAR(255) NOT NULL,
                    verification_code VARCHAR(16) NOT NULL,
                    verified BOOLEAN NOT NULL DEFAULT 0,
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
                    verified_at TIMESTAMP NULL,
                    last_sent_at TIMESTAMP NULL,
                    FOREIGN KEY (user_uuid) REFERENCES privileged_users(uuid) ON DELETE CASCADE ON UPDATE CASCADE
                );
                """;
        try (Statement statement = sqlSession.getConnection().createStatement()) {
            statement.execute(query);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    private static void createStaffApplicationsTable(@NotNull SqlSession sqlSession) {
        String query = """
                CREATE TABLE IF NOT EXISTS staff_applications (
                    id UUID NOT NULL DEFAULT (UUID()) PRIMARY KEY,
                    uuid UUID NOT NULL,
                    email VARCHAR(320) NOT NULL,
                    age INT NOT NULL,
                    discord_username VARCHAR(32) NOT NULL,
                    meets_requirements BOOLEAN NOT NULL,
                    pronouns VARCHAR(32) NULL,
                    join_date DATE NOT NULL,
                    weekly_playtime INT NOT NULL,
                    available_days TEXT NOT NULL,
                    available_times TEXT NOT NULL,
                    previous_experience TEXT NOT NULL,
                    plugin_experience TEXT NOT NULL,
                    moderator_expectations TEXT NOT NULL,
                    additional_info TEXT NULL,
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
                    send_at TIMESTAMP NULL,
                    assigned_to BIGINT UNSIGNED NULL,
                    FOREIGN KEY (uuid) REFERENCES privileged_users(uuid) ON DELETE CASCADE ON UPDATE CASCADE
                );
                """;
        try (Statement statement = sqlSession.getConnection().createStatement()) {
            statement.execute(query);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    private static void createAppealTable(@NotNull SqlSession sqlSession) {
        String query = """
                CREATE TABLE IF NOT EXISTS appeals (
                    id UUID NOT NULL DEFAULT (UUID()) PRIMARY KEY,
                    uuid UUID NOT NULL,
                    historyType VARCHAR(16) NOT NULL,
                    historyId BIGINT UNSIGNED NOT NULL,
                    username VARCHAR(16) NOT NULL,
                    reason TEXT NOT NULL,
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
                    send_at TIMESTAMP NULL,
                    e_mail TEXT NOT NULL,
                    assigned_to BIGINT UNSIGNED NULL,
                    FOREIGN KEY (uuid) REFERENCES privileged_users(uuid) ON DELETE CASCADE ON UPDATE CASCADE
                );
                """;
        try (Statement statement = sqlSession.getConnection().createStatement()) {
            statement.execute(query);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    private static void createdDiscordAppealTable(@NotNull SqlSession sqlSession) {
        String query = """
                CREATE TABLE IF NOT EXISTS discord_appeals (
                    id UUID NOT NULL DEFAULT (UUID()) PRIMARY KEY,
                    uuid UUID NOT NULL,
                    discord_id BIGINT UNSIGNED NOT NULL,
                    discord_username VARCHAR(32) NOT NULL,
                    reason TEXT NOT NULL,
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
                    send_at TIMESTAMP NULL,
                    e_mail TEXT NOT NULL,
                    assigned_to BIGINT UNSIGNED NULL,
                    FOREIGN KEY (uuid) REFERENCES privileged_users(uuid) ON DELETE CASCADE ON UPDATE CASCADE
                );
                """;
        try (Statement statement = sqlSession.getConnection().createStatement()) {
            statement.execute(query);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

}
