import { parse_addr } from '@scrow/tapscript/address';
import { exists } from './util.js';
import * as schema from '../schema/index.js';
export async function get_tx_data(host, txid) {
    const url = `${host}/api/tx/${txid}`;
    const res = await fetch(url);
    if (res.status === 404)
        return null;
    const json = await resolve_json(res);
    if (!json.ok)
        throw new Error(json.error);
    const parsed = await schema.oracle.txdata.spa(json.data);
    if (!parsed.success)
        throw new Error(parsed.error.toString());
    return parsed.data;
}
export async function get_utxo_state(host, txid, vout) {
    const url = `${host}/api/tx/${txid}/outspend/${vout}`;
    const res = await fetch(url);
    if (res.status === 404)
        return null;
    const json = await resolve_json(res);
    if (!json.ok)
        throw new Error(json.error);
    const parsed = await schema.oracle.txostate.spa(json.data);
    if (!parsed.success)
        throw new Error(parsed.error.toString());
    return parsed.data;
}
export async function get_utxo_data(host, query) {
    const { txid, vout, address } = query;
    const tx = await get_tx_data(host, txid);
    if (tx === null)
        return null;
    let idx;
    if (!exists(vout)) {
        if (!exists(address)) {
            throw new Error('You must specify an address or vout!');
        }
        idx = tx.vout.findIndex(e => e.scriptpubkey_address === address);
    }
    else {
        idx = vout;
    }
    if (idx === -1)
        return null;
    const txout = tx.vout.at(idx);
    if (txout === undefined)
        return null;
    const state = await get_utxo_state(host, txid, idx);
    if (state === null)
        return null;
    const txspend = {
        txid,
        vout: idx,
        value: txout.value,
        scriptkey: txout.scriptpubkey
    };
    return { txspend, status: tx.status, state };
}
export async function get_address_utxos(host, addr) {
    const url = `${host}/api/address/${addr}/utxo`;
    const res = await fetcher(url);
    if (!res.ok)
        throw new Error(res.error);
    const parsed = await schema.oracle.utxo.array().spa(res.data);
    if (!parsed.success)
        throw new Error(parsed.error.toString());
    return parsed.data.map(({ txid, status, value, vout }) => {
        const scriptkey = parse_addr(addr).hex;
        const state = { spent: false };
        const txspend = { txid, vout, value, scriptkey };
        return { state, status, txspend };
    });
}
export async function broadcast_tx(host, txhex) {
    const url = `${host}/api/tx`;
    const req = {
        body: txhex,
        headers: { 'content-type': 'text/plain' },
        method: 'POST'
    };
    const res = await fetch(url, req);
    const { status, statusText } = res;
    return (res.ok)
        ? { status, ok: true, data: await res.text() }
        : { status, ok: false, error: statusText };
}
export async function fee_estimates(host) {
    const url = `${host}/api/fee-estimates`;
    const res = await fetch(url);
    const json = await resolve_json(res);
    if (!json.ok)
        throw new Error(json.error);
    const ent = Object.entries(json.data);
    const rnd = ent.map(([k, v]) => [k, Math.ceil(v)]);
    return Object.fromEntries(rnd);
}
export async function get_fee_target(host, target) {
    const quotes = await fee_estimates(host);
    const index = String(target);
    const feerate = quotes[index];
    if (typeof feerate !== 'number') {
        throw new Error('No quote available for fee target: ' + index);
    }
    return feerate;
}
export async function fetcher(input, init, fetcher = fetch) {
    const res = await fetcher(input, init);
    return resolve_json(res);
}
export async function resolve_json(res) {
    const { status, statusText } = res;
    let data;
    try {
        data = await res.json();
    }
    catch {
        data = undefined;
    }
    if (!res.ok) {
        const error = (typeof data?.error === 'string')
            ? data.error
            : statusText;
        return { status, ok: false, error };
    }
    if (data === undefined) {
        return {
            status,
            ok: false,
            error: 'data is undefined'
        };
    }
    return { status, ok: true, data };
}
