const TIMEOUT_MS = 5000;
export class LicenseClient {
    serverUrl;
    licenseKey;
    constructor(serverUrl, licenseKey) {
        this.serverUrl = serverUrl;
        this.licenseKey = licenseKey;
    }
    async charge(documentHash, machineId, metadata) {
        return this.request('/v1/charge', {
            method: 'POST',
            body: { document_hash: documentHash, machine_id: machineId, ...metadata },
        });
    }
    async confirm(chargeId) {
        const res = await this.request('/v1/confirm', {
            method: 'POST',
            body: { charge_id: chargeId },
        });
        return res?.balance_cents ?? null;
    }
    async refund(chargeId) {
        const res = await this.request('/v1/refund', {
            method: 'POST',
            body: { charge_id: chargeId },
        });
        return res?.balance_cents ?? null;
    }
    async balance() {
        return this.request('/v1/balance', { method: 'GET' });
    }
    async setSpendingLimit(limitCents) {
        return this.request('/v1/spending-limit', {
            method: 'PUT',
            body: { limit_cents: limitCents },
        });
    }
    async sync(charges) {
        const res = await this.request('/v1/sync', {
            method: 'POST',
            body: { charges: charges.map((c) => ({ id: c.id, document_hash: c.document_hash, confirmed: c.confirmed === 1 })) },
        });
        if (!res)
            return null;
        return { balance_cents: res.balance_cents, rejected: res.rejected };
    }
    /** Fetch with an automatic abort timeout. */
    async fetchWithTimeout(url, init) {
        const controller = new AbortController();
        const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
        try {
            return await fetch(url, { ...init, signal: controller.signal });
        }
        finally {
            clearTimeout(timer);
        }
    }
    async request(path, opts) {
        try {
            const res = await this.fetchWithTimeout(`${this.serverUrl}${path}`, {
                method: opts.method,
                headers: {
                    'Content-Type': 'application/json',
                    'X-License-Key': this.licenseKey,
                },
                body: opts.body ? JSON.stringify(opts.body) : undefined,
            });
            if (!res.ok) {
                const data = await res.json().catch(() => ({}));
                if (res.status === 402) {
                    const d402 = data;
                    throw new InsufficientBalanceError(d402.balance_cents ?? 0, d402.cost_cents ?? 0);
                }
                if (res.status === 429) {
                    const d = data;
                    throw new SpendingLimitError(d.spent_cents ?? 0, d.limit_cents ?? 0, d.resets_at ?? '');
                }
                if (res.status === 401) {
                    throw new Error('Invalid license key');
                }
                return null;
            }
            return (await res.json());
        }
        catch (error) {
            // Re-throw known errors (balance, spending limit, auth)
            if (error instanceof InsufficientBalanceError)
                throw error;
            if (error instanceof SpendingLimitError)
                throw error;
            if (error instanceof Error && error.message === 'Invalid license key')
                throw error;
            // Network/timeout errors -> return null (caller decides how to handle)
            console.error(`[WARN] License server unreachable: ${error instanceof Error ? error.message : String(error)}`);
            return null;
        }
    }
}
export class InsufficientBalanceError extends Error {
    balanceCents;
    costCents;
    constructor(balanceCents, costCents) {
        const dollars = (balanceCents / 100).toFixed(2);
        const cost = (costCents / 100).toFixed(2);
        super(`Insufficient balance ($${dollars}). Each file costs $${cost}. Add funds at https://ocrprovenance.com/dashboard`);
        this.balanceCents = balanceCents;
        this.costCents = costCents;
        this.name = 'InsufficientBalanceError';
    }
}
export class SpendingLimitError extends Error {
    spentCents;
    limitCents;
    resetsAt;
    constructor(spentCents, limitCents, resetsAt) {
        super(`Monthly spending limit reached ($${(spentCents / 100).toFixed(2)} of $${(limitCents / 100).toFixed(2)} limit). ` +
            `Resets ${resetsAt}. Manage at https://ocrprovenance.com/dashboard/account`);
        this.spentCents = spentCents;
        this.limitCents = limitCents;
        this.resetsAt = resetsAt;
        this.name = 'SpendingLimitError';
    }
}
//# sourceMappingURL=client.js.map