import { ExecutionStatus } from "@lodestar/fork-choice";
import { ZERO_HASH_HEX, isForkPostDeneb, isForkPostFulu } from "@lodestar/params";
import { sszTypesFor } from "@lodestar/types";
import { toRootHex } from "@lodestar/utils";
import { getBlobKzgCommitments } from "../../../util/dataColumns.js";
import { isOptimisticBlock } from "../../../util/forkChoice.js";
import { getStateSlotFromBytes } from "../../../util/multifork.js";
import { getBlockResponse } from "../beacon/blocks/utils.js";
import { getStateResponseWithRegen } from "../beacon/state/utils.js";
import { assertUniqueItems } from "../utils.js";
export function getDebugApi({ chain, config, }) {
    return {
        async getDebugChainHeadsV2() {
            const heads = chain.forkChoice.getHeads();
            return {
                data: heads.map((block) => ({
                    slot: block.slot,
                    root: block.blockRoot,
                    executionOptimistic: isOptimisticBlock(block),
                })),
            };
        },
        async getDebugForkChoice() {
            return {
                data: {
                    justifiedCheckpoint: chain.forkChoice.getJustifiedCheckpoint(),
                    finalizedCheckpoint: chain.forkChoice.getFinalizedCheckpoint(),
                    forkChoiceNodes: chain.forkChoice.getAllNodes().map((node) => ({
                        slot: node.slot,
                        blockRoot: node.blockRoot,
                        parentRoot: node.parentRoot,
                        justifiedEpoch: node.justifiedEpoch,
                        finalizedEpoch: node.finalizedEpoch,
                        weight: node.weight,
                        validity: (() => {
                            switch (node.executionStatus) {
                                case ExecutionStatus.Valid:
                                case ExecutionStatus.PayloadSeparated:
                                    return "valid";
                                case ExecutionStatus.Invalid:
                                    return "invalid";
                                case ExecutionStatus.Syncing:
                                case ExecutionStatus.PreMerge:
                                    return "optimistic";
                            }
                        })(),
                        executionBlockHash: node.executionPayloadBlockHash ?? ZERO_HASH_HEX,
                    })),
                },
            };
        },
        async getProtoArrayNodes() {
            const nodes = chain.forkChoice.getAllNodes().map((node) => ({
                // if node has executionPayloadNumber, it will overwrite the below default
                executionPayloadNumber: 0,
                ...node,
                executionPayloadBlockHash: node.executionPayloadBlockHash ?? "",
                parent: String(node.parent),
                bestChild: String(node.bestChild),
                bestDescendant: String(node.bestDescendant),
            }));
            return { data: nodes };
        },
        async getStateV2({ stateId }, context) {
            const { state, executionOptimistic, finalized } = await getStateResponseWithRegen(chain, stateId);
            let slot, data;
            if (state instanceof Uint8Array) {
                slot = getStateSlotFromBytes(state);
                data = state;
            }
            else {
                slot = state.slot;
                data = context?.returnBytes ? state.serialize() : state.toValue();
            }
            return {
                data,
                meta: {
                    version: config.getForkName(slot),
                    executionOptimistic,
                    finalized,
                },
            };
        },
        async getDebugDataColumnSidecars({ blockId, indices }) {
            assertUniqueItems(indices, "Duplicate indices provided");
            const { block, executionOptimistic, finalized } = await getBlockResponse(chain, blockId);
            const fork = config.getForkName(block.message.slot);
            const blockRoot = sszTypesFor(fork).BeaconBlock.hashTreeRoot(block.message);
            let dataColumnSidecars;
            const blobCount = isForkPostDeneb(fork)
                ? getBlobKzgCommitments(fork, block).length
                : 0;
            if (isForkPostFulu(fork) && blobCount > 0) {
                dataColumnSidecars = await chain.getDataColumnSidecars(block.message.slot, toRootHex(blockRoot));
                if (dataColumnSidecars.length === 0) {
                    throw Error(`dataColumnSidecars not found in db for slot=${block.message.slot} root=${toRootHex(blockRoot)} blobs=${blobCount}`);
                }
            }
            else {
                dataColumnSidecars = [];
            }
            return {
                data: (indices
                    ? dataColumnSidecars.filter(({ index }) => indices.includes(index))
                    : dataColumnSidecars),
                meta: {
                    executionOptimistic,
                    finalized,
                    version: fork,
                },
            };
        },
    };
}
//# sourceMappingURL=index.js.map