#pragma once

#include "json.hpp"
#include <format>
#include <iostream>

namespace sliger::rpc {

struct Err {
    std::string msg;
};

template <typename T> class Res {
public:
    Res(T value)
        : holds_value(true)
        , value(std::move(value))
    {
    }
    Res(Err error)
        : holds_value(false)
        , error(error)
    {
    }
    auto is_ok() -> bool { return this->holds_value; }
    auto ok() & -> T& { return this->value; }
    auto ok() && -> T&& { return std::move(this->value); }
    auto err() -> Err { return this->error; }

private:
    bool holds_value;
    T value;
    Err error;
};

template <> class Res<void> {
public:
    Res()
        : holds_value(true)
    {
    }
    Res(Err error)
        : holds_value(false)
        , error(error)
    {
    }
    auto is_ok() -> bool { return this->holds_value; }
    auto err() -> Err { return this->error; }

private:
    bool holds_value;
    Err error;
};

class BracketFinder {
public:
    BracketFinder()
        : layers(0)
    {
    }
    auto feed(int8_t c)
    {
        if (c == '{') {
            this->layers += 1;
        } else if (c == '}') {
            this->layers -= 1;
        }
    }
    auto bracket_closed() -> bool { return this->layers == 0; }

private:
    int layers;
};

#define DONT_COPY_OR_MOVE(T)                                                   \
    T(const T&) = delete;                                                      \
    T operator=(const T&) = delete;                                            \
    T(T&&) = delete;                                                           \
    T operator=(T&&) = delete;

class Client;

class Socket {
public:
    DONT_COPY_OR_MOVE(Socket);
    Socket() = default;

    ~Socket()
    {
        if (this->initialized) {
            close(this->socket_fd);
        }
    }

    auto init() -> Res<void>;
    auto accept() -> Res<std::unique_ptr<Client>>;

private:
    int socket_fd = 0;
    bool initialized = false;
};

class Client {
public:
    DONT_COPY_OR_MOVE(Client);
    Client(int client_sock)
        : client_sock(client_sock)
    {
    }

    ~Client() { close(client_sock); }

    auto read(int8_t* buffer, size_t buffer_size) -> ssize_t;
    auto write(uint8_t* buffer, size_t buffer_size) -> ssize_t;
    void flush();

private:
    int client_sock;
};

class BufferedWriter {
public:
    BufferedWriter(Client& client)
        : client(&client)
    {
    }

    auto flush() -> Res<size_t>;
    auto write(uint8_t byte) -> Res<void>;
    auto write(std::string message) -> Res<void>;

private:
    static const size_t length = 1024;
    size_t occupied = 0;
    uint8_t buffer[length];
    Client* client;
};

/// - load code
///     - program input
/// - run
/// - run debug
///     - fwamegwaph option
///     - code covewage option
/// - fetch fwamegwaph
///     - json string
/// - fetch code covewage
///     - json string
/// - fetch stack
///     - json string
template <typename Functor> class RpcServer {
public:
    RpcServer(Functor&& functor)
        : functor(functor) { };

    auto listen() -> Res<void>
    {
        auto socket = Socket();
        if (auto res = socket.init(); not res.is_ok()) {
            return res.err();
        }
        while (true) {
            auto client_res = socket.accept();
            if (!client_res.is_ok()) {
                return client_res.err();
            }
            auto client = std::move(client_res.ok());

            const size_t buf_len = 1024;
            int8_t buffer[buf_len] = {};
            auto bracket_finder = BracketFinder();
            std::string message = {};
            while (true) {
                ssize_t bytes_read = client->read(buffer, buf_len);
                if (bytes_read <= 0) {
                    break;
                }

                for (size_t i = 0; i < (size_t)bytes_read; ++i) {
                    message += buffer[i];
                    bracket_finder.feed(buffer[i]);
                    if (!bracket_finder.bracket_closed()) {
                        continue;
                    }
                    auto req = sliger::json::parse_json(message);
                    if (!req.ok()) {
                        auto err = req.err();
                        auto msg = std::format(
                            "error parsing rpc message: '{}' @ {}:{}\n",
                            err.msg, err.pos.line, err.pos.col);
                        return Err {
                            .msg = msg,
                        };
                    }
                    auto writer = std::make_unique<BufferedWriter>(*client);
                    this->functor(std::move(req.val()), std::move(writer));
                    message.clear();
                    goto message_done;
                }
            }
        message_done:
        }
        std::unreachable();
    };

private:
    Functor functor;
};

};
