#pragma once

// LT models
#include "lt_schema.h"
#include "types.h"

// Standard C++ libraries
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <vector>

namespace lt {

//
// Errors
//

const error ErrEOF = "EOF";
const error ErrRedirect = "redirect";
const error ErrClosed = "use of closed connection";
inline bool ErrorIs(error err, error target) { return err.find(target) != std::string::npos; };
inline std::string RedirectLocation(error err) { return err.substr(std::string("redirect: ").length()); };

// ---------------------------------------------------------------------------
// Forward declarations
// ---------------------------------------------------------------------------

class PacketObject;
using Packet = std::shared_ptr<PacketObject>;

class WorkerObject;
using Worker = std::shared_ptr<WorkerObject>;

// Internal classes for buffers and round-tripper objects
class BufferObject;
using Buffer = std::shared_ptr<BufferObject>;

class RoundTripperObject;
using RoundTripper = std::shared_ptr<RoundTripperObject>;

// ---------------------------------------------------------------------------
// Packets
// ---------------------------------------------------------------------------

class PacketObject {
    friend class Client;

public:
    PacketObject(const json& j, error& err);
    ~PacketObject();

    int track = 0;
    std::string media = "";
    std::string signal = "";
    int64_t timestamp = 0;

    std::string ref = "";
    char* data;
    int length = 0;
    json meta;

private:
    std::vector<char> m_data;
    std::string m_handle;
    Buffer m_buf;
    RoundTripper m_roundTripper;
};

inline void to_json(json&, const Packet&) { } // Unused

inline void from_json(const json& j, Packet& packet)
{
    error err;
    packet.reset(new PacketObject(j, err));
    if (!err.empty()) {
        throw err;
    }
}

// ---------------------------------------------------------------------------
// Workers
// ---------------------------------------------------------------------------

class WorkerObject {
public:
    std::string name;
    std::string location;

    int64_t start;
    int64_t duration;
    int length;
    std::string status;

    std::vector<Packet> packets;
};

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(WorkerObject, name, location, start, duration, length, status, packets)

inline void to_json(json&, const Worker&) { } // Unused

inline void from_json(const json& j, Worker& worker)
{
    worker.reset(new WorkerObject());
    j.get_to(*worker);
}

// ---------------------------------------------------------------------------
// Client
// ---------------------------------------------------------------------------

class Client {
public:
    Client();

    //
    // GET request
    //
    // URL mandatory, response expected (JSON object)
    error Get(const std::string& url, json& response);
    // URL mandatory, no response expected
    error Get(const std::string& url, std::nullptr_t);

    // URL mandatory, response expected (parsed JSON)
    template <typename Response>
    error Get(const std::string& url, Response& response)
    {
        // Process request
        json r;
        error err = Get(url, r);
        if (!err.empty()) {
            return err;
        }
        // Parse response
        try {
            r.get_to(response);
        } catch (json::exception& e) {
            return e.what();
        }
        // Done
        return "";
    };
    // URL mandatory, no response expected
    error Get(const std::string& url, Worker& worker);

    //
    // POST request
    //

    // Body mandatory, response expected (JSON object)
    error Post(const std::string& url, const json& body, json& response);
    // Body mandatory, no response expected
    error Post(const std::string& url, const json& body, std::nullptr_t);
    // No body, response expected (JSON object)
    error Post(const std::string& url, std::nullptr_t, json& response);
    // No body, no response expected
    error Post(const std::string& url, std::nullptr_t, std::nullptr_t);

    // Body mandatory, response expected (parsed JSON)
    template <typename Body, typename Response>
    error Post(const std::string& url, const Body& body, Response& response)
    {
        // Process request
        json b = body, r;
        error err = Post(url, b, r);
        if (!err.empty()) {
            return err;
        }
        // Parse response
        try {
            r.get_to(response);
        } catch (json::exception& e) {
            return e.what();
        }
        // Done
        return "";
    };
    // Body mandatory, no response expected
    template <typename Body>
    error Post(const std::string& url, const Body& body, std::nullptr_t)
    {
        return Post(url, json(body), nullptr);
    };
    // No body, response expected (parsed JSON)
    template <typename Response>
    error Post(const std::string& url, std::nullptr_t, Response& response)
    {
        // Process request
        json r;
        error err = Post(url, nullptr, r);
        if (!err.empty()) {
            return err;
        }
        // Parse response
        try {
            r.get_to(response);
        } catch (json::exception& e) {
            return e.what();
        }
        // Done
        return "";
    };

    //
    // DELETE request
    //
    error Delete(const std::string& url);

private:
    error call(const std::string& method, const std::string& url, const json& body, json& response);

    RoundTripper m_roundTripper;
};


// ---------------------------------------------------------------------------
//
// Inline helper functions for one-shot API calls.
//
// These functions wrap common Client operations (GET, POST, DELETE)
// using a temporary Client instance, allowing simplified usage without setup.
// Template parameters handle optional request/response bodies
//
// ---------------------------------------------------------------------------

//
// GET request
//

// URL mandatory, response expected (parsed JSON)
template <typename Response>
error Get(const std::string& url, Response& response) { return Client().Get(url, response); }
// URL mandatory, no response expected
inline error Get(const std::string& url, std::nullptr_t) { return Client().Get(url, nullptr); }

//
// POST request
//

// Body mandatory, response expected (parsed JSON)
template <typename Body, typename Response>
error Post(const std::string& url, Body const& body, Response& response) { return Client().Post(url, body, response); }
// Body mandatory, no response expected
template <typename Body>
error Post(const std::string& url, Body const& body, std::nullptr_t) { return Client().Post(url, body, nullptr); }
// No body, response expected (parsed JSON)
template <typename Response>
error Post(const std::string& url, std::nullptr_t, Response& response) { return Client().Post(url, nullptr, response); }
// No body, no response expected
inline error Post(const std::string& url, std::nullptr_t, std::nullptr_t) { return Client().Post(url, nullptr, nullptr); }

//
// DELETE request
//

inline error Delete(const std::string& url) { return Client().Delete(url); }

} // namespace lt
