/* Copyright (c) 2009-2010 Satoshi Nakamoto
 * Copyright (c) 2009-2014 The Bitcoin Core developers
 * Copyright 2026 Rhett Creighton - Apache License 2.0
 * Distributed under the MIT software license, see the accompanying
 * file COPYING or http://www.opensource.org/licenses/mit-license.php. */

#ifndef ZCL_VALIDATION_CHAINSTATE_H
#define ZCL_VALIDATION_CHAINSTATE_H

#include "chain/chain.h"
#include <pthread.h>
#include "core/uint256.h"
#include "util/sync.h"
#include <stdbool.h>
#include <stdint.h>

#define MAX_BLOCK_MAP_SIZE 2000000
#define MAX_CHAIN_HEIGHT 2000000

struct block_file_info {
    unsigned int nBlocks;
    unsigned int nSize;
    unsigned int nUndoSize;
    unsigned int nHeightFirst;
    unsigned int nHeightLast;
    uint64_t nTimeFirst;
    uint64_t nTimeLast;
};

static inline void block_file_info_init(struct block_file_info *fi)
{
    memset(fi, 0, sizeof(*fi));
}

static inline void block_file_info_add_block(struct block_file_info *fi,
                                              unsigned int height,
                                              uint64_t block_time)
{
    if (fi->nBlocks == 0 || fi->nHeightFirst > height)
        fi->nHeightFirst = height;
    if (fi->nBlocks == 0 || fi->nTimeFirst > block_time)
        fi->nTimeFirst = block_time;
    fi->nBlocks++;
    if (height > fi->nHeightLast)
        fi->nHeightLast = height;
    if (block_time > fi->nTimeLast)
        fi->nTimeLast = block_time;
}

struct disk_tx_pos {
    struct disk_block_pos block_pos;
    unsigned int nTxOffset;
};

static inline void disk_tx_pos_init(struct disk_tx_pos *p)
{
    disk_block_pos_init(&p->block_pos);
    p->nTxOffset = 0;
}

struct block_map_entry {
    struct uint256 hash;
    struct block_index *index;
    bool occupied;
};

struct block_map {
    struct block_map_entry *buckets;
    size_t size;
    size_t capacity;
    pthread_rwlock_t rwlock;
};

void block_map_init(struct block_map *m);
void block_map_free(struct block_map *m);
struct block_index *block_map_find(const struct block_map *m,
                                    const struct uint256 *hash);
bool block_map_insert(struct block_map *m, const struct uint256 *hash,
                      struct block_index *index);
bool block_map_reserve(struct block_map *m, size_t expected_count);
size_t block_map_count(const struct block_map *m);

/* Iteration: call with *iter=0, returns false when done */
bool block_map_next(const struct block_map *m, size_t *iter,
                    const struct uint256 **hash_out,
                    struct block_index **index_out);

/* A chain[] array superseded by a window grow. Lock-free readers may still
 * hold the old pointer, so it is RETIRED (kept allocated, value-stable —
 * same discipline as block_index.hashBlock) and freed only by
 * active_chain_free. */
struct active_chain_retired {
    struct active_chain_retired *next;
    struct block_index **arr;
};

/* Concurrency contract: readers are lock-free. `chain`, `height` and
 * `capacity` are _Atomic; writers publish a grow as fully-populated array,
 * then capacity, then height LAST, so a reader that bounds-checks against
 * height/capacity and loads `chain` afterwards always sees an array that
 * spans its index, and a reader holding an older array only ever indexes
 * within that array's (smaller) span. Slot stores stay plain aligned
 * pointer writes (a racing reader sees the old or new block_index, both
 * never-freed). Writers serialize on `write_lock` — a leaf lock; nothing
 * else is acquired while it is held. */
struct active_chain {
    struct block_index **_Atomic chain;
    _Atomic int height;
    _Atomic int capacity;
    struct active_chain_retired *retired; /* guarded by write_lock */
    zcl_mutex_t write_lock;
};

void active_chain_init(struct active_chain *c);
void active_chain_free(struct active_chain *c);
struct block_index *active_chain_cached_tip(const struct active_chain *c);
struct block_index *active_chain_tip(const struct active_chain *c);
struct block_index *active_chain_at(const struct active_chain *c, int height);
bool active_chain_contains(const struct active_chain *c,
                           const struct block_index *bi);
/* Move the in-memory active-chain cache/window. This is not public tip
 * authority; reducer stages and explicit repair/bootstrap APIs publish
 * authority separately after their durable writes succeed. */
bool active_chain_move_window_tip(struct active_chain *c,
                                  struct block_index *bi);
/* Install `bi` as chain[bi->nHeight] and publish the height WITHOUT the
 * ancestor walk of active_chain_move_window_tip — the boot/restore repair
 * primitive (callers rebuild + disk-validate ancestry afterwards). The grow
 * follows the retire-not-free contract; height publishes LAST under
 * write_lock. Returns false only on grow allocation failure or bad args.
 * See chainstate.c. */
bool active_chain_install_tip_slot(struct active_chain *c,
                                   struct block_index *bi);
/* Forward-only widen of the visible chain[] window to a most-work candidate
 * that builds on the current tip, WITHOUT publishing an authoritative tip.
 * The reducer's structural analogue of assembling chain[] out to
 * find_most_work_chain's candidate. No-op when
 * candidate->nHeight <= c->height. See chainstate.c. */
bool active_chain_extend_window(struct active_chain *c,
                                struct block_index *candidate);
/* Forward-extend the window along the CONTIGUOUS have-data, script-validated
 * frontier above the finalized tip, bounded by max_height (caller passes the
 * utxo_apply cursor). Safe for tip_finalize's lookahead: it accepts a successor
 * only when its pprev is pointer-equal to the prior accepted block, so it never
 * exposes header-only/forked blocks and never overwrites a finalized slot (so
 * it cannot trigger the finalized-row false-reorg cascade a generic candidate
 * caused). No-op (no map scan) when max_height <= the window height.
 * See chainstate.c. */
bool active_chain_extend_window_have_data(struct active_chain *c,
                                          struct block_map *m, int max_height);
/* Side-effect-free most-work candidate selector over the block map, mirroring
 * find_most_work_chain's eligibility (failure-free, >= VALID_TREE, has data,
 * clean ancestry, refuses below-tip). Returns the current tip when no heavier
 * eligible candidate exists, or NULL only on NULL args. Used by the reducer's
 * window-extender; carries none of find_most_work_chain's gap-fill/logging
 * side effects so it is safe to call every stage tick. See chainstate.c. */
struct block_index *active_chain_most_work_candidate(struct active_chain *c,
                                                      struct block_map *m);
int active_chain_height(const struct active_chain *c);

struct active_chain_authority {
    int64_t (*get_height)(void);
    bool    (*get_hash)(uint8_t hash[32]);
    bool    (*is_authoritative)(void);
};

void active_chain_register_authority(const struct active_chain_authority *auth);
void active_chain_register_block_map(struct block_map *m);

struct chainstate {
    zcl_mutex_t cs_main;
    struct block_map map_block_index;
    struct active_chain chain_active;
    struct block_index *pindex_best_header;
    bool f_tx_index;
    bool f_reindex;
    bool f_importing;
    bool f_have_pruned;
    bool f_prune_mode;
    uint64_t n_prune_target;
};

void chainstate_init(struct chainstate *cs);
void chainstate_free(struct chainstate *cs);

struct block_index *chainstate_insert_block_index(struct chainstate *cs,
                                                   const struct uint256 *hash);

#endif
