/**
 * BlockRun phone API client.
 *
 * Thin wrapper over `/v1/phone/numbers/{list,buy,renew,release}` that
 * handles the x402 payment handshake using the wallet Franklin already
 * loads at startup. Pattern mirrors `src/tools/modal.ts` — first POST
 * returns 402 with payment requirements, we sign with the local wallet,
 * retry once with X-PAYMENT.
 *
 * Used by:
 *   - panel server (renew button, buy flow, refresh button)
 *   - phone cache refresh (background)
 *   - future Phone/Call tools surfaced to the agent
 */
import { getOrCreateWallet, getOrCreateSolanaWallet, createPaymentPayload, createSolanaPaymentPayload, parsePaymentRequired, extractPaymentDetails, solanaKeyToBytes, SOLANA_NETWORK, } from '@blockrun/llm';
import { loadChain, API_URLS, USER_AGENT } from '../config.js';
import { writeCache } from './cache.js';
function phoneEndpoint(chain, path) {
    return `${API_URLS[chain]}/v1/phone/${path}`;
}
async function extractPaymentReq(response) {
    let header = response.headers.get('payment-required');
    if (!header) {
        try {
            const body = (await response.clone().json());
            if (body.x402 || body.accepts)
                header = btoa(JSON.stringify(body));
        }
        catch { /* not JSON, no header */ }
    }
    return header;
}
async function signPayment(response, chain, endpoint, resourceDescription) {
    const paymentHeader = await extractPaymentReq(response);
    if (!paymentHeader)
        return null;
    if (chain === 'solana') {
        const wallet = await getOrCreateSolanaWallet();
        const paymentRequired = parsePaymentRequired(paymentHeader);
        const details = extractPaymentDetails(paymentRequired, SOLANA_NETWORK);
        const secretBytes = await solanaKeyToBytes(wallet.privateKey);
        const feePayer = details.extra?.feePayer || details.recipient;
        const payload = await createSolanaPaymentPayload(secretBytes, wallet.address, details.recipient, details.amount, feePayer, {
            resourceUrl: details.resource?.url || endpoint,
            resourceDescription: details.resource?.description || resourceDescription,
            maxTimeoutSeconds: details.maxTimeoutSeconds || 300,
            extra: details.extra,
        });
        return { 'PAYMENT-SIGNATURE': payload };
    }
    else {
        const wallet = getOrCreateWallet();
        const paymentRequired = parsePaymentRequired(paymentHeader);
        const details = extractPaymentDetails(paymentRequired);
        const payload = await createPaymentPayload(wallet.privateKey, wallet.address, details.recipient, details.amount, details.network || 'eip155:8453', {
            resourceUrl: details.resource?.url || endpoint,
            resourceDescription: details.resource?.description || resourceDescription,
            maxTimeoutSeconds: details.maxTimeoutSeconds || 300,
            extra: details.extra,
        });
        return { 'PAYMENT-SIGNATURE': payload };
    }
}
async function postWithPayment(endpoint, body, resourceDescription, timeoutMs = 30_000) {
    const chain = loadChain();
    const headers = {
        'Content-Type': 'application/json',
        'User-Agent': USER_AGENT,
    };
    const ctrl = new AbortController();
    const timer = setTimeout(() => ctrl.abort(), timeoutMs);
    try {
        const payload = JSON.stringify(body);
        let response = await fetch(endpoint, {
            method: 'POST',
            signal: ctrl.signal,
            headers,
            body: payload,
        });
        if (response.status === 402) {
            const paymentHeaders = await signPayment(response, chain, endpoint, resourceDescription);
            if (!paymentHeaders) {
                return { ok: false, status: 402, body: { error: 'payment signing failed' }, raw: '' };
            }
            response = await fetch(endpoint, {
                method: 'POST',
                signal: ctrl.signal,
                headers: { ...headers, ...paymentHeaders },
                body: payload,
            });
        }
        const raw = await response.text().catch(() => '');
        let parsed = {};
        try {
            parsed = raw ? JSON.parse(raw) : {};
        }
        catch { /* leave as {} */ }
        return { ok: response.ok, status: response.status, body: parsed, raw };
    }
    finally {
        clearTimeout(timer);
    }
}
/**
 * Fetch the wallet's owned numbers from BlockRun. Refreshes the local
 * cache on success so the terminal status bar picks up the same data.
 */
export async function listNumbers(opts) {
    const chain = loadChain();
    const result = await postWithPayment(phoneEndpoint(chain, 'numbers/list'), {}, 'List wallet-owned BlockRun phone numbers');
    if (!result.ok) {
        const message = typeof result.body.error === 'string' ? result.body.error : `gateway ${result.status}`;
        throw new Error(message);
    }
    const numbers = Array.isArray(result.body.numbers)
        ? result.body.numbers
        : [];
    writeCache({ wallet: opts.walletAddress, chain, numbers });
    return { numbers, count: numbers.length, paid: 0.001 };
}
export async function renewNumber(phoneNumber) {
    const chain = loadChain();
    const result = await postWithPayment(phoneEndpoint(chain, 'numbers/renew'), { phoneNumber }, `Renew BlockRun phone number ${phoneNumber}`);
    if (!result.ok) {
        const message = typeof result.body.error === 'string' ? result.body.error : `gateway ${result.status}`;
        throw new Error(message);
    }
    return {
        phone_number: String(result.body.phone_number ?? phoneNumber),
        expires_at: String(result.body.expires_at ?? ''),
        paid: 5.0,
    };
}
export async function buyNumber(opts) {
    const chain = loadChain();
    const body = { country: opts.country || 'US' };
    if (opts.areaCode)
        body.areaCode = opts.areaCode;
    const result = await postWithPayment(phoneEndpoint(chain, 'numbers/buy'), body, `Provision a new BlockRun phone number (${opts.country || 'US'})`);
    if (!result.ok) {
        const message = typeof result.body.error === 'string' ? result.body.error : `gateway ${result.status}`;
        throw new Error(message);
    }
    return {
        phone_number: String(result.body.phone_number ?? ''),
        expires_at: String(result.body.expires_at ?? ''),
        chain: result.body.chain || chain,
        paid: 5.0,
    };
}
export async function releaseNumber(phoneNumber) {
    const chain = loadChain();
    const result = await postWithPayment(phoneEndpoint(chain, 'numbers/release'), { phoneNumber }, `Release BlockRun phone number ${phoneNumber}`);
    if (!result.ok) {
        const message = typeof result.body.error === 'string' ? result.body.error : `gateway ${result.status}`;
        throw new Error(message);
    }
    return {
        released: Boolean(result.body.released),
        phone_number: String(result.body.phone_number ?? phoneNumber),
    };
}
