#ifndef SIHTTP_H
#define SIHTTP_H

#include "sihttp/bake_config.h"
#include "sijson.h"

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

#if defined(__GNUC__) || defined(__clang__)
#define SIHTTP_PRINTF_FORMAT(fmt, args) __attribute__((format(printf, fmt, args)))
#else
#define SIHTTP_PRINTF_FORMAT(fmt, args)
#endif

/* Opaque HTTP server handle. */
typedef struct sihttp_server_s sihttp_server_t;

/* User-defined application state.
 * Define struct sihttp_app_state_s in your application before accessing req->state fields.
 * The state is owned by the user and must outlive the server.
 */
typedef struct sihttp_app_state_s sihttp_app_state_t;

/* Response content type. AUTO defaults to text/plain for string bodies. */
typedef enum {
    SIHTTP_CONTENT_AUTO = 0,
    SIHTTP_CONTENT_TEXT,
    SIHTTP_CONTENT_JSON,
    SIHTTP_CONTENT_HTML,
    SIHTTP_CONTENT_BINARY,
} sihttp_content_type_t;

/* Handler response.
 * status defaults to 200 when set to 0.
 * body must be heap-allocated; the server takes ownership and frees it.
 */
typedef struct {
    int status;
    char *body;
    sihttp_content_type_t content_type;
} sihttp_response_t;

/* Incoming HTTP request passed to route handlers. */
typedef struct {
    const char *method;
    const char *path;
    const char *body;
    sihttp_app_state_t *state;
} sihttp_request_t;

typedef sihttp_response_t (*sihttp_handler_t)(const sihttp_request_t *);

/* HTTP methods. GET is the default method when the value is 0. */
typedef enum {
    SIHTTP_METHOD_GET = 0,
    SIHTTP_METHOD_POST,
    SIHTTP_METHOD_PUT,
    SIHTTP_METHOD_DELETE,
    SIHTTP_METHOD_OPTIONS,
} sihttp_method_t;

/* Route descriptor used by sihttp_route. */
typedef struct {
    sihttp_method_t method;
    sihttp_handler_t callback;
} sihttp_handler_desc_t;

/* Server configuration.
 * state is owned by the user and must outlive the server.
 * port 0 lets the OS choose a port; backlog uses a library default when set to 0.
 * max_requests_per_poll limits per-frame work; 0 uses a library default.
 */
typedef struct {
    int port;
    sihttp_app_state_t *state;
    int backlog;
    int max_requests_per_poll;
} sihttp_server_desc_t;

/* Server lifecycle. */
#define sihttp_server(...) sihttp_server_init(&(sihttp_server_desc_t)__VA_ARGS__)

SIHTTP_API sihttp_server_t *sihttp_server_init(const sihttp_server_desc_t *desc);
SIHTTP_API void sihttp_server_fini(sihttp_server_t *server);

/* Server runtime. host may be NULL to bind all IPv4 interfaces. */
SIHTTP_API int sihttp_server_listen(sihttp_server_t *server, const char *host, uint16_t port);
SIHTTP_API int sihttp_server_start(sihttp_server_t *server);
SIHTTP_API int sihttp_server_poll(sihttp_server_t *server);
SIHTTP_API int sihttp_server_run(sihttp_server_t *server);
SIHTTP_API void sihttp_server_stop(sihttp_server_t *server);
SIHTTP_API uint16_t sihttp_server_port(const sihttp_server_t *server);

/* Route registration. */
#define sihttp_route(server, path, ...)                                                            \
    sihttp_route_impl(server, path, &(sihttp_handler_desc_t)__VA_ARGS__)

SIHTTP_API void
sihttp_route_impl(sihttp_server_t *server, const char *path, const sihttp_handler_desc_t *desc);

SIHTTP_API void sihttp_get(sihttp_server_t *server, const char *path, sihttp_handler_t callback);
SIHTTP_API void sihttp_post(sihttp_server_t *server, const char *path, sihttp_handler_t callback);
SIHTTP_API void sihttp_put(sihttp_server_t *server, const char *path, sihttp_handler_t callback);
SIHTTP_API void sihttp_delete(sihttp_server_t *server, const char *path, sihttp_handler_t callback);

/* Route parameters are intentionally parsed as integers only. */
SIHTTP_API int64_t sihttp_param(const sihttp_request_t *req, const char *name);

/* Last library error for the current process. */
SIHTTP_API const char *sihttp_error(void);

/* printf-like formatter. Returns a heap-allocated string. */
SIHTTP_API char *siformat(const char *fmt, ...) SIHTTP_PRINTF_FORMAT(1, 2);

/* Build a response value with designated initializers. */
#define sihttp_response(...) ((sihttp_response_t)__VA_ARGS__)

#ifdef __cplusplus
}
#endif

#endif
