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

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

#include "Context.hpp"
#include "behavior/tasks/client/ClientTask.hpp"
#include "behavior/tasks/server/ServerTask.hpp"
#include "packets/base/Packet.hpp"
#include "packets/base/SecurityMode.hpp"
#include "packets/search/SearchPacketData.hpp"
#include "utils/network/network.hpp"


namespace drp::task {


void UndefinedTask::use(Context &context) {
    context.me.status = TaskType::UNDEFINED;
    context.server = nullptr;
    context.remotePeers.clear();
}


void UndefinedTask::handle(Context& context) {
    std::cout << "[Task - Undefined] List of peers: " << std::endl;
    for (const auto& remotePeer : context.remotePeers)
        std::cout <<
            "\tPeer(id=" << remotePeer->information.id << ", " <<
            "status=" << std::to_string(static_cast<std::uint8_t>(remotePeer->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->information.status == TaskType::SERVER; }
    );
    // if a server have been found, use it
    if (server != context.remotePeers.end()) {
        // go into client mode
        ClientTask::use(context, *server);
        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 std::shared_ptr<RemotePeer> serverCandidate = *std::ranges::max_element(
                context.remotePeers,
                [&](auto& remotePeer1, auto& remotePeer2) {
                    return (
                        (remotePeer1->information.serverEnabled ? remotePeer1->information.id : 0) <
                        (remotePeer2->information.serverEnabled ? remotePeer2->information.id : 0)
                    );
                }
            );

            // check if we are this peer
            if (util::network::is_localhost(serverCandidate->address, serverCandidate->addressLength)) {
                std::cout << "[Task - Undefined] Becoming server..." << std::endl;
                // go into server mode
                ServerTask::use(context, serverCandidate);
                return;
            }
        }

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

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

    // broadcast message
    packet.channel = 0;

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

    packetContent.eventType = event::EventType::SEARCH;
    packetContent.data = packetData.serialize();

    packet.setContent(context, packet::base::SecurityMode::PLAIN, packetContent);

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

    const auto serializedPacket = packet.serialize();

    // send the search message
    if (sendto(
        context.socket,
        serializedPacket.data(),
        serializedPacket.size(),
        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));
}


}
