#include "src/basic_proto_dispatcher.hpp"

extern SimpleLogger logger;

basic_proto_dispatcher::basic_proto_dispatcher(CActiveSocket* sock)
{
    this->SocketActive = sock;
    rcbuf = new uint8_t[MAX_PACKET];
}

int basic_proto_dispatcher::changeSocket(CActiveSocket* sock)
{
    this->SocketActive = sock;
    return 0;
}

int basic_proto_dispatcher::setMagicNumber(uint8_t magic)
{
    magic_number = magic;
    return magic;
}

int basic_proto_dispatcher::configureMsgpack(msgpack::sbuffer* sbuff_ptr, msgpack::packer<msgpack::sbuffer>* pk_ptr, msgpack::unpacker* upk_ptr)
{
    this->sbuff_ptr = sbuff_ptr;
    this->pk_ptr = pk_ptr;
    this->upk_ptr = upk_ptr;

    return 0;
}

uint32_t basic_proto_dispatcher::getErrorCode()
{
    return this->error_code;
}

uint32_t basic_proto_dispatcher::getStatusCode()
{
    return this->status_code;
}

uint32_t basic_proto_dispatcher::getNbytesSent()
{
    return this->nbytes_sent;
}

uint8_t basic_proto_dispatcher::getLastCommand()
{
    return this->command_code;
}

int basic_proto_dispatcher::sendValidatedMsgpackData()
{
    uint8_t* data = new uint8_t[sbuff_ptr->size()];
    std::copy(sbuff_ptr->data(), sbuff_ptr->data() + sbuff_ptr->size(), data);

    if(SocketActive->Send(data, sbuff_ptr->size()) == -1) {
        logger << LogPref::Flag(ERROR) << "Failed to send data to socket" << endl;
        sbuff_ptr->clear();
        upk_ptr->reset();
        return -1;
    }

    sbuff_ptr->clear();
    upk_ptr->reset();
    return 1;
}

int basic_proto_dispatcher::sendErrorMsg(std::uint32_t errCode, std::string errStr)
{
    pk_ptr->pack(magic_number);
    pk_ptr->pack((uint8_t) 0xFF);
    
    this->error_code = errCode;
    pk_ptr->pack(errCode);
    pk_ptr->pack(errStr);

    return sendValidatedMsgpackData();
}

int basic_proto_dispatcher::sendStatusMsg(std::uint32_t statCode, std::string statStr)
{
    pk_ptr->pack(magic_number);
    pk_ptr->pack((uint8_t) 0x01);
    
    this->error_code = statCode;
    pk_ptr->pack(statCode);
    pk_ptr->pack(statStr);

    return sendValidatedMsgpackData();
}

int basic_proto_dispatcher::recvValidatedHeader()
{
    int bytes_recieved = SocketActive->Receive(MAX_PACKET, rcbuf);

    if(bytes_recieved == 0)
    {
        logger << LogPref::Flag(ERROR) << "Client disconnected" << endl;
        stopSession();
        return 0;
    }

    #ifdef DEBUG
        logger << "Bytes recieved: " << bytes_recieved << endl;
    #endif

    upk_ptr->reserve_buffer(bytes_recieved);
    std::copy(rcbuf, rcbuf + bytes_recieved, upk_ptr->buffer());
    upk_ptr->buffer_consumed(bytes_recieved);

    msgpack::object_handle oh;
    msgpack::object obj;

    if(!upk_ptr->next(oh))
    {
        logger << LogPref::Flag(ERROR) << "Empty msgpack object!" << endl;
        sendErrorMsg(0x00000001, "Invalid msgpack object!" + std::to_string((int) command_code));
        return -1;
    }

    obj = oh.get();

    if(obj.as<uint8_t>() != this->magic_number)
    {
        logger << LogPref::Flag(ERROR) << "Invalid magic!" << endl;
        sendErrorMsg(0x00000002, "Invalid magic!");
        return -1;
    }
    if(!upk_ptr->next(oh))
    {
        logger << LogPref::Flag(ERROR) << "No command!" << endl;
        this->sendErrorMsg(0x00000003, "No command from client!");
        return -1;
    }

    obj = oh.get();
    this->command_code = obj.as<uint8_t>();
    if(command_callbacks.find(command_code) == command_callbacks.end())
    {
        logger << LogPref::Flag(ERROR) << "Command not allowed!" << endl;
        this->sendErrorMsg(0x00000004, "Not allowed command! CMD: " + std::to_string((int) command_code));
        return -1;
    }

    return 1;
}

int basic_proto_dispatcher::acceptRequest()
{
    logger << LogPref::Flag(INFO) << "Start handle request" << endl;

    int ret = recvValidatedHeader();
    switch(ret) {
        case 1:
            logger << "Validated data header recieved. Accepting command is allowed." << endl;
            break;
        case 0:
            logger << "Session stopped. Waiting for new connection." << endl;
            command_callbacks[0x0E]();
            command_callbacks[0x03]();
            return 0;
        case -1:
            logger << LogPref::Flag(ERROR) << "Error while validate data header." << endl;
            return -1;
    }

    logger << LogPref::Flag(INFO) << "Request header handled" << endl;

    ret = command_callbacks[command_code]();
    switch(ret) {
        case 1:
            logger << "Command was accepted. Wait next command." << endl;
            return 1;
        case 0:
            logger << "Command was accepted. Session stopped. Waiting for new connection." << endl;
            return 0;
        case -1:
            logger << LogPref::Flag(ERROR) << "Error while command is accepting." << endl;
            return -1;
    }
}

int basic_proto_dispatcher::stopSession()
{
    // Minimal implementation; expand to handle socket/session cleanup as needed.
    if (SocketActive) {
        SocketActive->Close();
    }
    return 0;
}