#include "UndefinedTask.hpp"
#include "../types.hpp"

#include <chrono>
#include <cstring>
#include <iostream>
#include <numeric>
#include <thread>
#include <algorithm>
#include <sys/socket.h>

#include "Context.hpp"
#include "packets/base/Packet.hpp"
#include "packets/base/SecurityMode.hpp"
#include "packets/search/SearchPacketData.hpp"


namespace drp::task {


void UndefinedTask::handle(Context& context) {
    std::cout << "[Task - Undefined] List of peers: " << std::endl;
    for (const auto& peer : context.remotePeers)
        std::cout <<
            "\tPeer(id=" << peer.second->information.id << ", " <<
            "status=" << std::to_string(static_cast<std::uint8_t>(peer.second->information.status)) << ")" <<
            std::endl;

    // search if a server is available among the peer.
    const auto& server = std::ranges::find_if(
        context.remotePeers,
        [&](const auto& peer) { return peer.second->information.status == TaskType::SERVER; }
    );

    // if a server have been found, use it
    if (server != context.remotePeers.end()) {
        // if a server have been found, use it
        context.server = server->second;
        context.me.status = TaskType::CLIENT;
        return;
    }

    // wait that no more new peers are being discovered
    if (
        std::chrono::high_resolution_clock::now() - context.latestPeerDiscovery >
        std::chrono::milliseconds(5000)
    ) {
        std::cout << "No more peers discovered." << std::endl;

        // otherwise, become the server if we have the highest ID.
        // TODO(Faraphel): should use the machine with the lowest average ping
        if (context.me.serverEnabled) {
            // find the remote peer with the highest id that can be a server
            const auto serverCandidate = std::ranges::max_element(
                context.remotePeers,
                [&](auto& remotePeer1, auto& remotePeer2) {
                    return (
                        (remotePeer1.second->information.serverEnabled ? remotePeer1.first : 0) <
                        (remotePeer2.second->information.serverEnabled ? remotePeer2.first : 0)
                    );
                }
            );

            // check if we are this peer
            if (context.me.id == serverCandidate->first) {
                std::cout << "[Task - Undefined] Becoming server..." << std::endl;
                // set ourselves as the server
                context.server = serverCandidate->second;
                context.me.status = TaskType::SERVER;
                return;
            }
        }

        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

    // prepare a search message
    packet::base::Packet packet {};
    packet::search::SearchPacketData packetData {};

    // broadcast message
    packet.channel = 0;
    packet.securityMode = static_cast<std::uint8_t>(packet::base::SecurityMode::PLAIN);

    // search message with the time of the message being sent
    packetData.timestamp = std::chrono::high_resolution_clock::now();

    packet.setContent(packetData.toGeneric());

    std::cout << "[Task - Undefined] Looking for new peers." << std::endl;

    // send the search message
    if (sendto(
        context.socket,
        &packet,
        sizeof(packet),
        0,
        context.broadcastAddressInfo->ai_addr,
        context.broadcastAddressInfo->ai_addrlen
    ) == -1)
        std::cerr << "[Task - Undefined] Could not send search event: " << strerror(errno) << std::endl;

    std::this_thread::sleep_for(std::chrono::seconds(1));
}


}
