#include <iostream>
#include <filesystem>
#include <Utilities/ServerInit.hpp>
#include <Utilities/Utilities.hpp>
#include <Serialize/Serialize.hpp>
#include <Utilities/ConsoleColor.hpp>
#include <UserDB/UserDB.hpp>
#include <WorldIndex/WorldIndex.hpp>
#include <WorldType/SurvivalWorldType/SurvivalWorldType.hpp>
#include <AtomicDataStore/AtomicDataStore.hpp>

static void checkWorldState()
{
    if (std::filesystem::exists("server/world.voxelverse"))
    {
        std::cout << ConsoleColor::Green << "The world already exists." << ConsoleColor::Reset << std::endl <<
            ConsoleColor::Yellow << "Note:" << ConsoleColor::Reset << " To create a brand new world, you can delete the " <<
            ConsoleColor::Magenta << "server/world.voxelverse" << ConsoleColor::Reset << " file and re-run " <<
            ConsoleColor::Magenta << "--init" << ConsoleColor::Reset << "." << std::endl << std::endl;
        
        return;
    };

    std::cout << ConsoleColor::Yellow << "The world does not yet exist; will create now." << ConsoleColor::Reset << std::endl;
    std::optional<uint64_t> seed;

    for (;;)
    {
        std::cout << "Enter the world seed to use (leave empty to auto-generate): ";
        std::cout.flush();

        std::string line;
        std::getline(std::cin, line);

        if (line.size() == 0)
        {
            seed = std::nullopt;
            break;
        }
        else
        {
            try
            {
                seed = std::stoull(line);
                break;
            }
            catch (const std::exception &e)
            {
                std::cout << ConsoleColor::Red << "Please enter a number (or leave empty)" << ConsoleColor::Reset << std::endl;
            };
        };
    };

    std::cout << "Creating the world ..." << std::endl;

    // Create the world file.
    AtomicDataStore store;
    store.create("server/world.voxelverse");
    const std::string &storeError = store.getError();

    if (!storeError.empty())
    {
        std::cout << ConsoleColor::Red << storeError << ConsoleColor::Reset << std::endl;
    }

    WorldIndex worldIndex = WorldIndex::create(false, WorldAvatar::Voxelverse);
    std::shared_ptr<WorldTypeSettings> settings = SurvivalWorldType::createSettingsWithSeed(seed);
    worldIndex.setSettings("main:survival", *settings);

    AtomicDataStore::Plan plan;
    serializeInto(plan["windex.dat"], worldIndex);
    store.update(plan, 0);

    std::cout << ConsoleColor::Green << "World created." << ConsoleColor::Reset << std::endl << std::endl;
};

static void checkUserDB()
{
    if (std::filesystem::exists("server/users.db"))
    {
        std::cout << ConsoleColor::Green << "The user database already exists." << ConsoleColor::Reset << std::endl <<
            ConsoleColor::Yellow << "Note:" << ConsoleColor::Reset << " To wipe and re-create the user database, delete the " <<
            ConsoleColor::Magenta << "server/users.db" << ConsoleColor::Reset << " file and re-run " <<
            ConsoleColor::Magenta << "--init" << ConsoleColor::Reset << "." << std::endl << std::endl;
        
        return;
    };

    std::cout << ConsoleColor::Yellow << "The user database does not yet exist. Will create it now." <<
        ConsoleColor::Reset << std::endl;

    std::string adminUserName;
    std::string adminPassword;

    for (;;)
    {
        std::cout << "Enter a user name for the admin: ";
        std::cout.flush();
        std::getline(std::cin, adminUserName);

        if (adminUserName == "")
        {
            std::cout << ConsoleColor::Red << "The username cannot be empty!" << ConsoleColor::Reset << std::endl;
            continue;
        };

        adminPassword = inputPassword("Enter the password: ");
        std::string password2 = inputPassword("Re-type password: ");

        if (adminPassword == "")
        {
            std::cout << ConsoleColor::Red << "Password cannot be empty." << ConsoleColor::Reset << std::endl;
            continue;
        };

        if (adminPassword != password2)
        {
            std::cout << ConsoleColor::Red << "Passwords did not match." << ConsoleColor::Reset << std::endl;
            continue;
        };

        // Everything OK.
        break;
    };

    std::cout << "Creating user database ..." << std::endl;
    std::filesystem::create_directories("server");

    std::cout << "Creating the admin user " << ConsoleColor::Magenta << adminUserName <<
        ConsoleColor::Reset << " ..." << std::endl;

    UserDB userDB("server/users.db");
    userDB.create();
    UserDB::CreateUserResult createUserResult = userDB.createUser(adminUserName, adminPassword, true);

    if (!createUserResult.created)
    {
        std::cout << ConsoleColor::Red << "Failed to create admin user: " << createUserResult.message <<
            ConsoleColor::Reset << std::endl;
        crash("Failed to create admin user.");
    };

    std::cout << ConsoleColor::Green << "User database created." << ConsoleColor::Reset << std::endl << std::endl;
};

void serverInit()
{
    std::cout << ConsoleColor::Cyan << "Welcome to the interactive server initialization tool." <<
        ConsoleColor::Reset << std::endl << std::endl;

    checkWorldState();
    checkUserDB();

    std::cout << "Server init tasks completed." << ConsoleColor::Reset << std::endl;
};