// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
// SPDX-License-Identifier: AGPL-3.0-or-later

/**
 * @file sqlite_format.cpp
 * @brief SQLite3 on-disk file-format parsers (see sqlite_format.h).
 */

#include "services/sqlite/sqlite_format.h"

#if DETWS_ENABLE_SQLITE

#include <string.h>

namespace
{
uint16_t be16(const uint8_t *p)
{
    return (uint16_t)(((uint16_t)p[0] << 8) | p[1]);
}
uint32_t be32(const uint8_t *p)
{
    return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | (uint32_t)p[3];
}
// The 16-byte file magic, including the trailing NUL.
const char SQLITE_MAGIC[16] = {'S', 'Q', 'L', 'i', 't', 'e', ' ', 'f', 'o', 'r', 'm', 'a', 't', ' ', '3', '\0'};

bool is_pow2(uint32_t v)
{
    return v && (v & (v - 1)) == 0;
}
} // namespace

size_t sqlite_varint_decode(const uint8_t *buf, size_t len, uint64_t *out)
{
    uint64_t v = 0;
    for (size_t i = 0; i < 8; i++)
    {
        if (i >= len)
            return 0; // incomplete varint
        v = (v << 7) | (uint8_t)(buf[i] & 0x7f);
        if ((buf[i] & 0x80) == 0)
        {
            *out = v;
            return i + 1;
        }
    }
    if (len < 9)
        return 0;
    v = (v << 8) | buf[8]; // 9th byte carries all 8 bits
    *out = v;
    return 9;
}

uint64_t sqlite_serial_type_size(uint64_t t)
{
    switch (t)
    {
    case 0:
        return 0; // NULL
    case 1:
        return 1;
    case 2:
        return 2;
    case 3:
        return 3;
    case 4:
        return 4;
    case 5:
        return 6;
    case 6:
        return 8;
    case 7:
        return 8; // IEEE 754 float64
    case 8:
        return 0; // integer 0
    case 9:
        return 0; // integer 1
    case 10:
    case 11:
        return 0; // reserved for internal use
    default:
        // >=12 even -> BLOB of (t-12)/2; >=13 odd -> TEXT of (t-13)/2. Floor division covers both.
        return t >= 12 ? (t - 12) / 2 : 0;
    }
}

bool sqlite_parse_db_header(const uint8_t *buf, size_t len, SqliteDbHeader *out)
{
    if (len < 100 || memcmp(buf, SQLITE_MAGIC, 16) != 0)
        return false;

    uint16_t raw_ps = be16(buf + 16);
    uint32_t page_size = (raw_ps == 1) ? 65536u : raw_ps;
    // A valid page size is a power of two in [512, 65536].
    if (page_size < 512 || page_size > 65536 || !is_pow2(page_size))
        return false;

    out->page_size = page_size;
    out->write_version = buf[18];
    out->read_version = buf[19];
    out->reserved_per_page = buf[20];
    out->file_change_counter = be32(buf + 24);
    out->page_count = be32(buf + 28);
    out->freelist_first = be32(buf + 32);
    out->freelist_count = be32(buf + 36);
    out->schema_cookie = be32(buf + 40);
    out->schema_format = be32(buf + 44);
    out->text_encoding = be32(buf + 56);
    out->user_version = be32(buf + 60);
    out->application_id = be32(buf + 68);
    out->sqlite_version = be32(buf + 96);
    return true;
}

bool sqlite_parse_btree_header(const uint8_t *page, size_t page_len, size_t offset, SqliteBtreeHeader *out)
{
    if (offset + 8 > page_len)
        return false;
    const uint8_t *p = page + offset;
    uint8_t type = p[0];
    bool interior = (type == SQLITE_BTREE_INTERIOR_INDEX || type == SQLITE_BTREE_INTERIOR_TABLE);
    bool leaf = (type == SQLITE_BTREE_LEAF_INDEX || type == SQLITE_BTREE_LEAF_TABLE);
    if (!interior && !leaf)
        return false;
    uint8_t hdr = interior ? 12 : 8;
    if (offset + hdr > page_len)
        return false;

    out->type = type;
    out->first_freeblock = be16(p + 1);
    out->cell_count = be16(p + 3);
    uint16_t ccs = be16(p + 5);
    out->cell_content_start = (ccs == 0) ? 65536u : ccs;
    out->frag_free_bytes = p[7];
    out->right_most_page = interior ? be32(p + 8) : 0;
    out->header_size = hdr;
    return true;
}

uint32_t sqlite_cell_pointer(const uint8_t *page, size_t page_len, const SqliteBtreeHeader *bh, size_t page_offset,
                             uint16_t i)
{
    if (i >= bh->cell_count)
        return 0;
    size_t at = page_offset + bh->header_size + (size_t)i * 2;
    if (at + 2 > page_len)
        return 0;
    return be16(page + at); // cell pointers are offsets from the start of the page
}

bool sqlite_parse_table_leaf_cell(const uint8_t *page, size_t page_len, uint32_t page_size, uint8_t reserved,
                                  uint32_t cell_off, SqliteTableLeafCell *out)
{
    if (cell_off >= page_len)
        return false;
    uint64_t payload_len = 0, rowid = 0;
    size_t n1 = sqlite_varint_decode(page + cell_off, page_len - cell_off, &payload_len);
    if (n1 == 0)
        return false;
    size_t n2 = sqlite_varint_decode(page + cell_off + n1, page_len - cell_off - n1, &rowid);
    if (n2 == 0)
        return false;

    // SQLite overflow threshold for a table b-tree leaf (fileformat2.html section 1.6).
    uint32_t usable = page_size - reserved;
    uint32_t max_local = usable - 35;
    uint32_t min_local = ((usable - 12) * 32) / 255 - 23;
    uint32_t local;
    if (payload_len <= max_local)
    {
        local = (uint32_t)payload_len;
    }
    else
    {
        uint32_t k = min_local + (uint32_t)((payload_len - min_local) % (usable - 4));
        local = (k <= max_local) ? k : min_local;
    }

    out->rowid = rowid;
    out->payload_len = (uint32_t)payload_len;
    out->local_off = cell_off + (uint32_t)(n1 + n2);
    out->local_len = local;
    out->has_overflow = local < payload_len;
    if (out->local_off + local > page_len)
        return false; // the claimed local payload does not fit the page
    return true;
}

bool sqlite_read_payload(SqlitePageReader read, void *ctx, uint32_t page_size, uint8_t reserved,
                         const uint8_t *leaf_page, const SqliteTableLeafCell *cell, uint8_t *out, uint32_t out_cap,
                         uint8_t *work_page)
{
    if (cell->payload_len > out_cap)
        return false; // fail closed rather than overrun the caller buffer
    if ((size_t)cell->local_off + cell->local_len > page_size)
        return false;

    memcpy(out, leaf_page + cell->local_off, cell->local_len);
    uint32_t got = cell->local_len;
    if (!cell->has_overflow)
        return got == cell->payload_len; // wholly in-page: nothing to follow

    // The 4-byte first-overflow-page number sits immediately after the local prefix.
    size_t ptr_off = (size_t)cell->local_off + cell->local_len;
    if (ptr_off + 4 > page_size)
        return false;
    uint32_t next = be32(leaf_page + ptr_off);

    uint32_t usable = page_size - reserved;
    uint32_t content = usable - 4; // per overflow page: 4-byte next pointer + content
    if (content == 0)
        return false;
    // A chain cannot legitimately be longer than this; the bound turns a corrupt/looping
    // next-pointer into a clean failure instead of an unbounded read.
    uint32_t max_pages = cell->payload_len / content + 2;

    for (uint32_t pages = 0; next != 0 && got < cell->payload_len; pages++)
    {
        if (pages >= max_pages)
            return false; // broken / looping chain
        if (!read(ctx, next, work_page, page_size))
            return false;
        uint32_t nnext = be32(work_page);
        uint32_t chunk = cell->payload_len - got;
        if (chunk > content)
            chunk = content;
        if (got + chunk > out_cap)
            return false;
        memcpy(out + got, work_page + 4, chunk);
        got += chunk;
        next = nnext;
    }
    return got == cell->payload_len;
}

bool sqlite_record_begin(SqliteRecordCursor *c, const uint8_t *rec, uint32_t rec_len)
{
    uint64_t hdr_len = 0;
    size_t n = sqlite_varint_decode(rec, rec_len, &hdr_len);
    if (n == 0 || hdr_len > rec_len || hdr_len < n)
        return false;
    c->rec = rec;
    c->rec_len = rec_len;
    c->hdr_pos = (uint32_t)n;
    c->hdr_end = (uint32_t)hdr_len;
    c->val_pos = (uint32_t)hdr_len;
    return true;
}

bool sqlite_record_next(SqliteRecordCursor *c, uint64_t *serial_type, const uint8_t **val, uint32_t *val_len)
{
    if (c->hdr_pos >= c->hdr_end)
        return false;
    uint64_t st = 0;
    size_t n = sqlite_varint_decode(c->rec + c->hdr_pos, c->hdr_end - c->hdr_pos, &st);
    if (n == 0)
        return false;
    uint32_t sz = (uint32_t)sqlite_serial_type_size(st);
    if (c->val_pos + sz > c->rec_len)
        return false; // value runs past the record (a truncated / overflowing row)
    *serial_type = st;
    *val = c->rec + c->val_pos;
    *val_len = sz;
    c->hdr_pos += (uint32_t)n;
    c->val_pos += sz;
    return true;
}

int64_t sqlite_column_int(uint64_t serial_type, const uint8_t *val, uint32_t val_len)
{
    if (serial_type == 8)
        return 0;
    if (serial_type == 9)
        return 1;
    if (serial_type < 1 || serial_type > 6)
        return 0;
    size_t nbytes = (size_t)sqlite_serial_type_size(serial_type);
    if (val_len < nbytes)
        return 0;
    uint64_t u = 0;
    for (size_t i = 0; i < nbytes; i++)
        u = (u << 8) | val[i];
    size_t bits = nbytes * 8;
    if (bits < 64 && (u & (1ull << (bits - 1))))
        u |= ~((1ull << bits) - 1); // sign-extend from the stored width
    return (int64_t)u;
}

double sqlite_column_float(const uint8_t *val, uint32_t val_len)
{
    if (val_len < 8)
        return 0.0;
    uint64_t u = 0;
    for (int i = 0; i < 8; i++)
        u = (u << 8) | val[i];
    double d = 0.0;
    memcpy(&d, &u, 8); // u holds the big-endian-read IEEE-754 bit pattern as a native u64
    return d;
}

namespace
{
// The page byte offset of an interior page's b-tree header (100 only for page 1, the schema root).
size_t page_hdr_off(uint32_t pgno)
{
    return pgno == 1 ? 100 : 0;
}

// Child page pointer to descend for child index `i` of an interior table page: the left-child of cell i,
// or the right-most pointer when i == cell_count. 0 on a bad pointer.
uint32_t interior_child(const uint8_t *page, size_t page_len, const SqliteBtreeHeader *h, size_t off, uint16_t i)
{
    if (i >= h->cell_count)
        return h->right_most_page;
    uint32_t cp = sqlite_cell_pointer(page, page_len, h, off, i);
    if (cp == 0 || (size_t)cp + 4 > page_len)
        return 0;
    return be32(page + cp); // an interior-table cell starts with the u32 left-child page number
}

// Descend leftmost from `pgno`, pushing interior frames, until a leaf table page is loaded into c->leaf.
bool cursor_descend(SqliteTableCursor *c, uint32_t pgno)
{
    for (;;)
    {
        if (!c->read(c->ctx, pgno, c->work, c->page_size))
            return false;
        size_t off = page_hdr_off(pgno);
        SqliteBtreeHeader h;
        if (!sqlite_parse_btree_header(c->work, c->page_size, off, &h))
            return false;
        if (h.type == SQLITE_BTREE_LEAF_TABLE)
        {
            memcpy(c->leaf, c->work, c->page_size);
            c->leaf_hdr = h;
            c->leaf_off = (uint32_t)off;
            c->leaf_pgno = pgno;
            c->leaf_count = h.cell_count;
            c->leaf_cell = 0;
            return true;
        }
        if (h.type != SQLITE_BTREE_INTERIOR_TABLE)
            return false; // an index b-tree or garbage - not a table scan
        if (c->depth >= SQLITE_BTREE_MAX_DEPTH)
            return false;
        uint32_t child = interior_child(c->work, c->page_size, &h, off, 0);
        if (child == 0)
            return false;
        c->stack_pg[c->depth] = pgno;
        c->stack_idx[c->depth] = 1; // child 0 taken; next is 1
        c->depth++;
        pgno = child;
    }
}
} // namespace

bool sqlite_table_cursor_begin(SqliteTableCursor *c, SqlitePageReader read, void *ctx, uint32_t page_size,
                               uint8_t reserved, uint32_t rootpage, uint8_t *leaf_buf, uint8_t *work_buf)
{
    c->read = read;
    c->ctx = ctx;
    c->page_size = page_size;
    c->reserved = reserved;
    c->leaf = leaf_buf;
    c->work = work_buf;
    c->ovf_buf = nullptr;
    c->ovf_cap = 0;
    c->depth = 0;
    c->leaf_count = 0;
    c->leaf_cell = 0;
    return cursor_descend(c, rootpage);
}

void sqlite_table_cursor_set_overflow_buf(SqliteTableCursor *c, uint8_t *buf, uint32_t cap)
{
    c->ovf_buf = buf;
    c->ovf_cap = cap;
}

bool sqlite_table_cursor_next(SqliteTableCursor *c, uint64_t *rowid, SqliteRecordCursor *row)
{
    for (;;)
    {
        if (c->leaf_cell < c->leaf_count)
        {
            uint32_t cp = sqlite_cell_pointer(c->leaf, c->page_size, &c->leaf_hdr, c->leaf_off, c->leaf_cell);
            c->leaf_cell++;
            if (cp == 0)
                continue;
            SqliteTableLeafCell cell;
            if (!sqlite_parse_table_leaf_cell(c->leaf, c->page_size, c->page_size, c->reserved, cp, &cell))
                continue;
            if (cell.has_overflow && c->ovf_buf)
            {
                // Reassemble the full record into the caller's overflow buffer, then read columns from it.
                // c->work is free here (we are at a leaf, not descending), so it serves as the scratch page.
                if (!sqlite_read_payload(c->read, c->ctx, c->page_size, c->reserved, c->leaf, &cell, c->ovf_buf,
                                         c->ovf_cap, c->work))
                    continue;
                if (!sqlite_record_begin(row, c->ovf_buf, cell.payload_len))
                    continue;
            }
            else if (!sqlite_record_begin(row, c->leaf + cell.local_off, cell.local_len))
                continue;
            *rowid = cell.rowid;
            return true;
        }
        // Current leaf is exhausted: advance the descent stack to the next subtree.
        if (c->depth == 0)
            return false; // whole table consumed
        int top = c->depth - 1;
        if (!c->read(c->ctx, c->stack_pg[top], c->work, c->page_size))
            return false;
        size_t off = page_hdr_off(c->stack_pg[top]);
        SqliteBtreeHeader h;
        if (!sqlite_parse_btree_header(c->work, c->page_size, off, &h))
            return false;
        if (c->stack_idx[top] <= h.cell_count)
        {
            uint32_t child = interior_child(c->work, c->page_size, &h, off, c->stack_idx[top]);
            c->stack_idx[top]++;
            if (child == 0 || !cursor_descend(c, child))
                return false;
        }
        else
        {
            c->depth--; // this interior frame's children are all visited; pop to its parent
        }
    }
}

#endif // DETWS_ENABLE_SQLITE
