/* eslint-disable no-console */
// Assemble full block JSON from /block, /block_results, and decoded txs.

import type { BlockJson, TxsResult, AbciEvent } from "../types.js";
import type { RpcClient } from "../rpc/client.js";
import { normalizeEvents, buildCombinedLogs } from "../normalize/events.js";
import { sha256Hex, base64ToBytes, bytesToHex } from "../utils/bytes.js";
import { deepConvertKeys, CaseMode } from "../utils/case.js";
import {stripLarge} from "../utils/stripLarge.js";

/** Extracts chain_id, height, and time (ISO) from /block response */
function getMeta(b: any) {
    const header = b?.block?.header ?? {};
    return {
        chain_id: String(header.chain_id ?? ""),
        height: String(header.height ?? b?.block?.last_commit?.height ?? ""),
        time: String(header.time ?? ""),
    };
}

/** Safe getter for txs (base64 strings) from /block */
function getTxsBase64(b: any): string[] {
    const txs = b?.block?.data?.txs;
    return Array.isArray(txs) ? txs.filter((x) => typeof x === "string") : [];
}

/** Safe getter for txs_results from /block_results matching tx count */
function getTxsResults(br: any, count: number): TxsResult[] {
    const arr = Array.isArray(br?.txs_results) ? br.txs_results : [];
    // Tendermint may omit txs_results when txs=0
    if (arr.length === count) return arr as TxsResult[];
    // Pad/truncate to match txs length to keep indexes aligned
    const out: TxsResult[] = [];
    for (let i = 0; i < count; i++) out.push(arr[i] ?? { code: 0, events: [] });
    return out;
}

/** Assemble tx-level object with decoded body/auth_info and response fields */
export async function assembleTxObjects(
    heightISOTime: string,
    txsB64: string[],
    decoded: any[],
    br: any,
    caseMode: CaseMode = "snake"
) {
    const results = getTxsResults(br, txsB64.length);
    const out: BlockJson["txs"] = [];

    for (let i = 0; i < txsB64.length; i++) {
        const rawB64 = txsB64[i];
        const rawBytes = base64ToBytes(rawB64);
        const hash = await sha256Hex(rawBytes);

        const decodedTx = decoded[i] ?? {
            "@type": "/cosmos.tx.v1beta1.Tx",
            body: { messages: [], memo: "", timeout_height: "0", unordered: false, timeout_timestamp: null, extension_options: [], non_critical_extension_options: [] },
            auth_info: { signer_infos: [], fee: { amount: [], gas_limit: "0", payer: "", granter: "" }, tip: null },
            signatures: [],
        };

        const msgs = Array.isArray(decodedTx?.body?.messages)
            ? decodedTx.body.messages.map((m: any) => deepConvertKeys(m, caseMode))
            : [];

        const body = {
            ...decodedTx.body,
            messages: msgs,
        };

        const r = results[i] ?? ({} as TxsResult);
        const txLevelEvents: AbciEvent[] = normalizeEvents(r?.events as any[]);
        const logs = buildCombinedLogs(String(r?.log ?? ""), txLevelEvents);

        out.push({
            index: i,
            hash, // uppercase hex
            raw: { base64: rawB64, hex: bytesToHex(rawBytes).toUpperCase() },
            decoded: { ...decodedTx, body },
            tx_response: {
                height: String(br?.height ?? ""),
                codespace: String(r?.codespace ?? ""),
                code: Number(r?.code ?? 0),
                data: String(r?.data ?? ""), // base64
                raw_log: String(r?.log ?? ""),
                logs,
                events: txLevelEvents,
                gas_wanted: String(r?.gas_wanted ?? ""),
                gas_used: String(r?.gas_used ?? ""),
                timestamp: heightISOTime,
            },
        });
    }

    return out;
}



// add this new function:
export async function assembleBlockJsonFromParts(
    rpc: RpcClient,
    blockResp: any,
    blockResultsResp: any,
    decodedTxs: any[],
    caseMode: CaseMode = "snake"
): Promise<BlockJson> {
    const meta = getMeta(blockResp);
    const txsB64 = getTxsBase64(blockResp);
    const txObjs = await assembleTxObjects(meta.time, txsB64, decodedTxs, blockResultsResp, caseMode);
    return {
        meta: {
            chain_id: meta.chain_id,
            height: meta.height,
            time: meta.time,
        },
        block: stripLarge(blockResp),
        block_results: stripLarge(blockResultsResp),
        txs: txObjs,
    };
}

// keep the old convenience wrapper intact:
export async function assembleBlockJson(
    rpc: RpcClient,
    height: number,
    decodedTxs: any[],
    caseMode: CaseMode = "snake"
): Promise<BlockJson> {
    const [b, br] = await Promise.all([rpc.fetchBlock(height), rpc.fetchBlockResults(height)]);
    return assembleBlockJsonFromParts(rpc, b, br, decodedTxs, caseMode);
}