#include <Events/PlayerJoinEvent.h>
#include <Events/PlayerLeaveEvent.h>
#include <Events/UpdateEvent.h>
#include <GameServer.h>
#include <Services/ServerListService.h>

#include <console/Setting.h>
#include <future>

#define CPPHTTPLIB_OPENSSL_SUPPORT
#include <httplib.h>

extern Console::Setting<uint32_t> uMaxPlayerCount;

static constexpr char kMasterServerEndpoint[] =
    //"http://127.0.0.1:8000";
    "https://skyrim-reborn-list.skyrim-together.com";

static Console::Setting bAnnounceServer{"LiveServices:bAnnounceServer", "Whether to list the server on the public server list", false};

ServerListService::ServerListService(World& aWorld, entt::dispatcher& aDispatcher) noexcept
    : m_world(aWorld)
    , m_updateConnection(aDispatcher.sink<UpdateEvent>().connect<&ServerListService::OnUpdate>(this))
    , m_nextAnnounce(std::chrono::seconds(0))
{
    if (!bAnnounceServer)
        spdlog::warn("bAnnounceServer is set to false. The server will not show up as a public server. "
                     "If you are just playing with friends, this is probably what you want.");
}

void ServerListService::OnUpdate(const UpdateEvent& acEvent) noexcept
{
    if (m_nextAnnounce < std::chrono::steady_clock::now())
    {
        Announce();

        m_nextAnnounce = (std::chrono::steady_clock::now() + std::chrono::minutes(1));
    }
}

void ServerListService::OnPlayerJoin(const PlayerJoinEvent& acEvent) noexcept
{
    Announce();

    m_nextAnnounce = (std::chrono::steady_clock::now() + std::chrono::minutes(1));
}

void ServerListService::OnPlayerLeave(const PlayerLeaveEvent& acEvent) noexcept
{
    Announce();

    m_nextAnnounce = (std::chrono::steady_clock::now() + std::chrono::minutes(1));
}

void ServerListService::Announce() noexcept
{
    std::thread([&]() {
        auto* pServer = GameServer::Get();
        auto pc = static_cast<uint16_t>(m_world.GetPlayerManager().Count());

        if (bAnnounceServer)
            m_flags |= kIsPublic;
        if (pServer->IsPasswordProtected())
            m_flags |= kHasPassword;

        const auto& cInfo = pServer->GetInfo();

        PostAnnouncement(cInfo.name, cInfo.desc, cInfo.icon_url, pServer->GetPort(), cInfo.tick_rate, pc,
                         uMaxPlayerCount.value_as<uint16_t>(), cInfo.tagList, bAnnounceServer,
                         pServer->IsPasswordProtected(), m_flags);
    }).detach();
}

void ServerListService::PostAnnouncement(String acName, String acDesc, String acIconUrl, uint16_t aPort, uint16_t aTick,
                                         uint16_t aPlayerCount, uint16_t aPlayerMaxCount, String acTagList,
                                         bool aPublic, bool aPassword, int32 aFlags) noexcept
{
    const std::string kVersion{BUILD_COMMIT};
    const httplib::Params params{
        {"name", std::string(acName.c_str(), acName.size())},
        {"desc", std::string(acDesc.c_str(), acDesc.size())},
        {"icon_url", std::string(acIconUrl.c_str(), acIconUrl.size())},
        {"version", std::string(kVersion.c_str(), kVersion.size())},
        {"port", std::to_string(aPort)},
        {"tick", std::to_string(aTick)},
        {"player_count", std::to_string(aPlayerCount)},
        {"max_player_count", std::to_string(aPlayerMaxCount)},
        {"tags", std::string(acTagList.c_str(), acTagList.size())},
        {"public", aPublic ? "true" : "false"},
        {"pass", aPassword ? "true" : "false"},
        {"flags", std::to_string(aFlags)},
    };

    httplib::Client client(kMasterServerEndpoint);
    client.enable_server_certificate_verification(false);
    client.set_read_timeout(std::chrono::milliseconds(30000));
    const auto response = client.Post("/announce", params);

    // If we send a 403 it means we banned this server
    if (response)
    {
        if (response->status == 403)
            GameServer::Get()->Kill();
        else if (response->status != 200)
            spdlog::error("Server list error! {}", response->body);
    }
    else
    {
        spdlog::error("Server could not reach the server list! {}", to_string(response.error()));
    }
}
