import { Buff } from '@cmdcode/buff';
import { hash340, sha512 } from '@cmdcode/crypto-tools/hash';
import { tweak_pubkey } from '@cmdcode/crypto-tools/keys';
import { get_deposit_ctx } from './deposit.js';
import { create_ctx, get_nonce_ctx, verify_psig, } from '@cmdcode/musig2';
import { create_sighash, create_tx_tmpl, create_txinput, parse_txinput } from './tx.js';
import { get_entry, sort_bytes } from './util.js';
export function create_session(agent, record_id) {
    const pnonce = get_session_pnonce(agent.id, record_id, agent);
    return {
        agent_id: agent.id,
        agent_pk: agent.pubkey,
        agent_pn: pnonce.hex
    };
}
export function create_covenant(context, contract, signer, txout) {
    const { agent_id, cid, agent_pn } = contract;
    const pnonce = get_session_pnonce(agent_id, cid, signer).hex;
    const pnonces = [pnonce, agent_pn];
    const mupaths = get_path_mutexes(context, contract, pnonces, txout);
    const psigs = create_psigs(mupaths, signer);
    return { cid, pnonce, psigs };
}
export function create_return(address, deposit, signer, txfee) {
    const { agent_id, dpid, agent_pn, value } = deposit;
    const pnonce = get_session_pnonce(agent_id, dpid, signer).hex;
    const pnonces = [pnonce, agent_pn];
    const txhex = create_tx_tmpl(address, value - txfee);
    const mutex = get_return_mutex(deposit, pnonces, txhex);
    const psig = create_mutex_psig(mutex, signer);
    return { dpid, pnonce, psig, txhex };
}
export function get_path_mutexes(context, contract, pnonces, txout) {
    const { agent_id, cid, outputs } = contract;
    const sid = get_session_id(agent_id, cid);
    return outputs.map(([label, vout]) => {
        const txinput = create_txinput(txout);
        const mut_ctx = get_mutex_ctx(context, vout, pnonces, sid, txinput);
        return [label, mut_ctx];
    });
}
export function get_return_mutex(deposit, pnonces, txhex) {
    const { agent_id, agent_pk, dpid, member_pk, sequence } = deposit;
    const dep_ctx = get_deposit_ctx(agent_pk, member_pk, sequence);
    const sid = get_session_id(agent_id, dpid);
    const txinput = parse_txinput(deposit);
    return get_mutex_ctx(dep_ctx, txhex, pnonces, sid, txinput);
}
export function get_mutex_ctx(context, output, pnonces, sid, txinput) {
    const { key_data, tap_data } = context;
    const group_pub = key_data.group_pubkey;
    const sighash = create_sighash(txinput, output);
    const nonce_twk = get_session_tweak(sid, pnonces, sighash);
    const pubnonces = tweak_pnonces(pnonces, nonce_twk);
    const nonce_ctx = get_nonce_ctx(pubnonces, group_pub, sighash);
    const musig_opt = { key_tweaks: [tap_data.taptweak] };
    const musig_ctx = create_ctx(key_data, nonce_ctx, musig_opt);
    return {
        sid,
        mutex: musig_ctx,
        tweak: nonce_twk
    };
}
export function get_session_id(agent_id, record_id) {
    return sha512(agent_id, record_id);
}
export function get_session_pnonce(agent_id, record_id, signer) {
    const sid = get_session_id(agent_id, record_id);
    const pn1 = signer.gen_nonce(sid.subarray(0, 32));
    const pn2 = signer.gen_nonce(sid.subarray(32, 64));
    return Buff.join([pn1, pn2]);
}
export function get_session_tweak(sid, pnonces, sighash) {
    const pns = Buff.join(sort_bytes(pnonces));
    return hash340('contract/session', sid, pns, sighash);
}
export function tweak_pnonces(keys, tweak) {
    return keys.map(e => tweak_pnonce(e, tweak));
}
export function tweak_pnonce(key, tweak) {
    const pnonces = Buff
        .parse(key, 32, 64)
        .map(k => tweak_pubkey(k, [tweak], true));
    return Buff.join(pnonces);
}
export function create_psigs(mutexes, signer) {
    return mutexes.map(([label, ctx]) => {
        return [label, create_mutex_psig(ctx, signer)];
    });
}
export function create_mutex_psig(context, signer) {
    const { sid, mutex, tweak } = context;
    const opt = { nonce_tweak: tweak };
    return signer.musign(mutex, sid, opt).hex;
}
export function verify_mutex_psigs(mutexes, psigs) {
    for (const [label, ctx] of mutexes) {
        const psig = get_entry(label, psigs);
        if (!verify_mutex_psig(ctx, psig)) {
            throw new Error('psig failed validation for path: ' + label);
        }
    }
}
export function verify_mutex_psig(ctx, psig) {
    return verify_psig(ctx.mutex, psig);
}
