/*
 * pg_stub.c — Stub definitions for PostgreSQL server symbols.
 *
 * On macOS 26+ (Tahoe), dyld eagerly resolves all flat namespace symbols at
 * load time.  pgrx extensions reference ~28 PostgreSQL server-internal symbols
 * (MemoryContexts, SPI functions, etc.) that are normally provided by the
 * `postgres` executable when the extension is loaded as a shared library.
 *
 * When we run `cargo test --lib` to execute pure-Rust unit tests, there is no
 * postgres process — so those symbols are undefined and dyld aborts with:
 *
 *     dyld: symbol not found in flat namespace '_CacheMemoryContext'
 *
 * This stub provides NULL / no-op definitions for every PostgreSQL symbol the
 * test binary references.  It is compiled into `libpg_stub.dylib` and injected
 * via DYLD_INSERT_LIBRARIES when running unit tests.
 *
 * IMPORTANT: None of these stubs are ever _called_ during unit tests — the
 * tests exercise pure Rust logic only.  If a test accidentally calls a PG
 * function it will get a NULL pointer / zero return, which will crash or
 * fail the test (the desired behaviour).
 *
 * Regenerate the symbol list with:
 *   nm target/debug/deps/pg_trickle-* | grep ' U _' | awk '{print $NF}' \
 *     | grep -E '^_(Alloc|Cache|Copy|Cur|Current|err|Error|format_type|Free|Get|get_|Is|Mem|Message|palloc|palloc_|pfree|PG_|parse_|pg_|Portal|Postmaster|raw_|repalloc|SPI_|Top)'
 */

#include <stddef.h>
#include <stdint.h>

/* ── MemoryContext globals (all NULL) ─────────────────────────────────── */
void *CacheMemoryContext        = NULL;
void *CurrentMemoryContext      = NULL;
void *CurTransactionContext     = NULL;
void *ErrorContext              = NULL;
void *MessageContext            = NULL;
void *PortalContext             = NULL;
void *PostmasterContext         = NULL;
void *TopMemoryContext          = NULL;
void *TopTransactionContext     = NULL;

/* ── Error handling globals ───────────────────────────────────────────── */
void *error_context_stack       = NULL;
void *PG_exception_stack        = NULL;

/* ── Memory allocation functions ──────────────────────────────────────── */
void *palloc(size_t size)                       { (void)size; return NULL; }
void *palloc0(size_t size)                      { (void)size; return NULL; }
void *palloc_extended(size_t size, int flags)   { (void)size; (void)flags; return NULL; }
void *repalloc(void *pointer, size_t size)      { (void)pointer; (void)size; return NULL; }

/* ── MemoryContext functions ──────────────────────────────────────────── */
void *AllocSetContextCreateInternal(void *parent, const char *name,
                                    size_t minContextSize,
                                    size_t initBlockSize,
                                    size_t maxBlockSize) {
    (void)parent; (void)name;
    (void)minContextSize; (void)initBlockSize; (void)maxBlockSize;
    return NULL;
}

void  MemoryContextDelete(void *ctx)            { (void)ctx; }
void *MemoryContextGetParent(void *ctx)         { (void)ctx; return NULL; }
void  pfree(void *ptr)                          { (void)ptr; }

/* ── List helpers ─────────────────────────────────────────────────────── */
void *lappend(void *list, void *datum)          { (void)datum; return list; }
void *lcons(void *datum, void *list)            { (void)datum; return list; }
void  list_free(void *list)                     { (void)list; }
void  list_free_deep(void *list)                { (void)list; }

/* ── Error data ──────────────────────────────────────────────────────── */
void *CopyErrorData(void)                       { return NULL; }
void  FreeErrorData(void *edata)                { (void)edata; }

/* ── Error reporting functions ───────────────────────────────────────── */
int   errcode(int sqlerrcode)                   { (void)sqlerrcode; return 0; }
int   errmsg(const char *fmt, ...)              { (void)fmt; return 0; }
int   errdetail(const char *fmt, ...)           { (void)fmt; return 0; }
int   errhint(const char *fmt, ...)             { (void)fmt; return 0; }
int   errcontext_msg(const char *fmt, ...)      { (void)fmt; return 0; }
int   errstart(int elevel, const char *domain)  { (void)elevel; (void)domain; return 0; }
void  errfinish(const char *filename, int lineno, const char *funcname) {
    (void)filename; (void)lineno; (void)funcname;
}

/* ── Transaction / type helpers ───────────────────────────────────────── */
int      GetDatabaseEncoding(void)             { return 0; }
uint32_t GetCurrentTransactionId(void)          { return 0; }
uint32_t GetCurrentTransactionIdIfAny(void)     { return 0; }
int16_t  get_typlen(uint32_t typid)            { (void)typid; return -1; }
_Bool    get_typbyval(uint32_t typid)          { (void)typid; return 0; }
void     get_typlenbyval(uint32_t typid, int16_t *typlen, _Bool *typbyval) {
    (void)typid;
    if (typlen) {
        *typlen = -1;
    }
    if (typbyval) {
        *typbyval = 0;
    }
}
void     get_typlenbyvalalign(uint32_t typid, int16_t *typlen,
                              _Bool *typbyval, char *typalign) {
    (void)typid;
    if (typlen) {
        *typlen = -1;
    }
    if (typbyval) {
        *typbyval = 0;
    }
    if (typalign) {
        *typalign = 'i';
    }
}
uint32_t get_array_type(uint32_t typid)         { (void)typid; return 0; }
int   IsBinaryCoercible(uint32_t a, uint32_t b) { (void)a; (void)b; return 0; }
char *format_type_extended(uint32_t oid, int32_t typmod, int flags) {
    (void)oid; (void)typmod; (void)flags;
    return NULL;
}

/* ── Toast helpers ────────────────────────────────────────────────────── */
void *pg_detoast_datum(void *datum)             { return datum; }
void *pg_detoast_datum_copy(void *datum)        { return datum; }
void *pg_detoast_datum_slice(void *datum, int32_t first, int32_t count) {
    (void)first;
    (void)count;
    return datum;
}
void *pg_detoast_datum_packed(void *datum)      { return datum; }

/* ── Type output helpers ──────────────────────────────────────────────── */
uintptr_t byteaout(void *fcinfo)                { (void)fcinfo; return 0; }
uintptr_t textout(void *fcinfo)                 { (void)fcinfo; return 0; }
uintptr_t json_out(void *fcinfo)                { (void)fcinfo; return 0; }
uintptr_t jsonb_out(void *fcinfo)               { (void)fcinfo; return 0; }

/* ── Parser entry points ─────────────────────────────────────────────── */
void *raw_parser(const char *str, int mode) {
    (void)str; (void)mode;
    return NULL;
}

void *parse_analyze_fixedparams(void *parse_tree,
                                const char *source_text,
                                const uint32_t *param_types,
                                int num_params,
                                void *query_env) {
    (void)parse_tree; (void)source_text;
    (void)param_types; (void)num_params; (void)query_env;
    return NULL;
}

/* ── SPI functions ───────────────────────────────────────────────────── */
int      SPI_connect(void)                      { return -1; }
int      SPI_finish(void)                       { return -1; }
int      SPI_execute(const char *cmd, int ro, long cnt) {
    (void)cmd; (void)ro; (void)cnt;
    return -1;
}
int      SPI_execute_with_args(const char *cmd, int nargs,
                               void *argtypes, void *values,
                               const char *nulls, int ro, long cnt) {
    (void)cmd; (void)nargs; (void)argtypes; (void)values;
    (void)nulls; (void)ro; (void)cnt;
    return -1;
}
int      SPI_fnumber(void *tupdesc, const char *fname) {
    (void)tupdesc; (void)fname;
    return -1;
}
char    *SPI_fname(void *tupdesc, int fnumber) {
    (void)tupdesc; (void)fnumber;
    return NULL;
}
char    *SPI_getvalue(void *tuple, void *tupdesc, int fnumber) {
    (void)tuple; (void)tupdesc; (void)fnumber;
    return NULL;
}
void    *SPI_getbinval(void *tuple, void *tupdesc, int fnumber,
                       int *isnull) {
    (void)tuple; (void)tupdesc; (void)fnumber;
    if (isnull) *isnull = 1;
    return NULL;
}
char    *SPI_gettype(void *tupdesc, int fnumber) {
    (void)tupdesc; (void)fnumber;
    return NULL;
}
uint32_t SPI_gettypeid(void *tupdesc, int fnumber) {
    (void)tupdesc; (void)fnumber;
    return 0;
}

/* SPI globals */
uint64_t SPI_processed = 0;
void    *SPI_tuptable  = NULL;

/* ── sigsetjmp stub ──────────────────────────────────────────────────── */
/*
 * pgrx's PG_exception_stack references sigsetjmp indirectly. On macOS the
 * symbol is provided by libSystem so it should always resolve, but we
 * include it here defensively.
 */
