/**
 * Indexer HTTP Client
 *
 * TypeScript equivalent of the Rust indexer API client.
 * Makes direct HTTP calls to the indexer service.
 */
import { BaseClient } from './base-client';
/** Page size for unspent-notes pagination (server maximum). */
const UNSPENT_NOTES_PAGE_SIZE = 500;
/**
 * Hard cap on pages fetched to protect against runaway loops
 * (PAGE_SIZE * MAX_PAGES = 50,000 notes).
 */
const UNSPENT_NOTES_MAX_PAGES = 100;
export class IndexerClient extends BaseClient {
    // The registered-token list is a global on-chain registry that changes
    // rarely (only when a new token is added). Cache the result for the
    // lifetime of the client and dedupe concurrent fetches — first-login
    // flows fan out into 5+ parallel callers (balances, fees, notes, history).
    registeredTokensCache = null;
    registeredTokensInflight = null;
    async getServerInfo() {
        return this.request('/api/v1/info');
    }
    async getTokenByAddress(address) {
        return this.request(`/api/v1/tokens/${encodeURIComponent(address)}`);
    }
    async getRegisteredTokens() {
        if (this.registeredTokensCache)
            return this.registeredTokensCache;
        if (this.registeredTokensInflight)
            return this.registeredTokensInflight;
        this.registeredTokensInflight = this.request('/api/v1/tokens')
            .then((raw) => {
            this.registeredTokensCache = raw.tokens;
            return raw.tokens;
        })
            .finally(() => {
            this.registeredTokensInflight = null;
        });
        return this.registeredTokensInflight;
    }
    /**
     * Drop the cached registered-token list. Call when a new token has been
     * registered on-chain and callers need to see it without restarting.
     */
    invalidateRegisteredTokens() {
        this.registeredTokensCache = null;
    }
    async getBalance(accountId) {
        return this.request(`/indexer/balance/${encodeURIComponent(accountId)}`);
    }
    async getUnspentNotes(pubkey, tokenAddress) {
        const allNotes = [];
        let offset = 0;
        let pagesFetched = 0;
        let truncatedNext;
        // Paginate through all unspent notes to avoid truncation at the
        // server's default limit (100). Operations like unshield/transfer
        // need the full note set for correct balance computation and note selection.
        for (;;) {
            const query = new URLSearchParams({
                spent: 'false',
                limit: String(UNSPENT_NOTES_PAGE_SIZE),
                offset: String(offset),
            });
            if (tokenAddress) {
                query.set('token', tokenAddress);
            }
            const page = await this.request(`/api/v1/wallets/${encodeURIComponent(pubkey)}/notes?${query.toString()}`);
            const pageLen = page.data.length;
            const next = page.next;
            allNotes.push(...page.data);
            pagesFetched++;
            if (!next || pageLen < UNSPENT_NOTES_PAGE_SIZE)
                break;
            if (pagesFetched >= UNSPENT_NOTES_MAX_PAGES) {
                // Surface truncation via the returned `next` field instead of
                // silently returning a partial set.
                console.warn(`[IndexerClient] getUnspentNotes hit max-pages safety cap ` +
                    `(pages=${pagesFetched}, fetched=${allNotes.length}); result is truncated`);
                truncatedNext = next;
                break;
            }
            offset += pageLen;
        }
        return { data: allNotes, limit: allNotes.length, next: truncatedNext };
    }
    async getTreeState() {
        return this.request('/indexer/tree');
    }
    async getFees(tokenId) {
        const qs = tokenId != null ? `?token_id=${tokenId}` : '';
        return this.request(`/api/v1/fees${qs}`);
    }
    async submitTransfer(request) {
        return this.request('/api/v1/transactions/transfers', { method: 'POST', body: JSON.stringify(request) });
    }
    async submitWithdrawal(request) {
        return this.request('/api/v1/transactions/withdrawals', { method: 'POST', body: JSON.stringify(request) });
    }
    async getShieldStatus(requestId) {
        return this.request(`/api/v1/transactions/shields/${encodeURIComponent(requestId)}`);
    }
    async getTransferStatus(id) {
        return this.request(`/api/v1/transactions/transfers/${encodeURIComponent(id)}`);
    }
    async getUnshieldStatus(id) {
        return this.request(`/api/v1/transactions/unshields/${encodeURIComponent(id)}`);
    }
    async getTransactionHistory(pubkey, params) {
        const query = new URLSearchParams();
        if (params?.type)
            query.set('type', params.type);
        if (params?.limit)
            query.set('limit', String(params.limit));
        if (params?.cursor)
            query.set('cursor', params.cursor);
        const qs = query.toString();
        const path = `/api/v1/wallets/${encodeURIComponent(pubkey)}/transactions${qs ? `?${qs}` : ''}`;
        return this.request(path);
    }
    async lookupIdentity(identifier) {
        return this.request(`/api/v1/users/${encodeURIComponent(identifier)}`);
    }
    async getWalletBalance(mpk, tokenAddress) {
        const params = new URLSearchParams({ token: tokenAddress });
        return this.request(`/api/v1/wallets/${encodeURIComponent(mpk)}?${params}`);
    }
    async getWalletBalances(mpk) {
        return this.request(`/api/v1/wallets/${encodeURIComponent(mpk)}`);
    }
    async getWalletTransactions(mpk, params) {
        const query = new URLSearchParams();
        if (params?.txType)
            query.set('txType', params.txType);
        if (params?.tokenId != null)
            query.set('tokenId', String(params.tokenId));
        if (params?.direction)
            query.set('direction', params.direction);
        if (params?.limit)
            query.set('limit', String(params.limit));
        if (params?.cursor)
            query.set('cursor', params.cursor);
        const qs = query.toString();
        const path = `/api/v1/wallets/${encodeURIComponent(mpk)}/transactions${qs ? `?${qs}` : ''}`;
        return this.request(path);
    }
    async getAppInfo(appIds) {
        return this.request('/api/v1/apps/info', {
            method: 'POST',
            body: JSON.stringify({ appIds }),
        });
    }
}
//# sourceMappingURL=indexer-client.js.map