import { logger } from "../logger.js";
import { writeFile } from "node:fs/promises";
/** Extract the most useful error message, unwrapping `cause` chains from undici/fetch. */
function describeError(err) {
    if (!(err instanceof Error))
        return String(err);
    // Node's undici wraps the real error in `cause` — e.g. "fetch failed" hides ECONNREFUSED
    const cause = err.cause;
    if (cause instanceof Error) {
        return `${err.message}: ${cause.message}`;
    }
    return err.message;
}
/**
 * HTTP client for the VisionClaw onboarding service.
 * All methods are non-fatal: errors are logged but never thrown,
 * so a downed onboarding service never blocks the agent.
 */
export class OnboardingServiceClient {
    baseUrl;
    agentName;
    apiKey;
    constructor(baseUrl, agentName, apiKey) {
        this.baseUrl = baseUrl.replace(/\/+$/, "");
        this.agentName = encodeURIComponent(agentName);
        this.apiKey = apiKey;
    }
    async heartbeat(payload) {
        try {
            const res = await fetch(`${this.baseUrl}/api/agents/${this.agentName}/heartbeat`, {
                method: "POST",
                headers: {
                    Authorization: `Bearer ${this.apiKey}`,
                    ...(payload ? { "Content-Type": "application/json" } : {}),
                },
                ...(payload ? { body: JSON.stringify(payload) } : {}),
                signal: AbortSignal.timeout(10_000),
            });
            if (!res.ok) {
                logger.warn(`Onboarding service heartbeat failed: HTTP ${res.status}`);
                return null;
            }
            return (await res.json());
        }
        catch (err) {
            logger.warn(`Onboarding service heartbeat error: ${describeError(err)}`);
            return null;
        }
    }
    /**
     * Heartbeat variant that throws on auth errors (401/403) instead of
     * silently returning null. Used for the startup key-validation heartbeat.
     */
    async heartbeatStrict(payload) {
        const res = await fetch(`${this.baseUrl}/api/agents/${this.agentName}/heartbeat`, {
            method: "POST",
            headers: {
                Authorization: `Bearer ${this.apiKey}`,
                ...(payload ? { "Content-Type": "application/json" } : {}),
            },
            ...(payload ? { body: JSON.stringify(payload) } : {}),
            signal: AbortSignal.timeout(10_000),
        });
        if (res.status === 401 || res.status === 403) {
            throw new Error(`Onboarding service rejected API key: HTTP ${res.status}`);
        }
        if (!res.ok) {
            logger.warn(`Onboarding service heartbeat failed: HTTP ${res.status}`);
            return null;
        }
        return (await res.json());
    }
    /**
     * Ask the onboarding service to reset the owner for this agent.
     * Returns true if the server confirmed the reset, false otherwise.
     * Non-fatal: a failed call logs a warning but never throws.
     */
    async resetOwner() {
        try {
            const res = await fetch(`${this.baseUrl}/api/agents/${this.agentName}/reset`, {
                method: "POST",
                headers: { Authorization: `Bearer ${this.apiKey}` },
                signal: AbortSignal.timeout(10_000),
            });
            if (!res.ok) {
                logger.warn(`Onboarding service reset failed: HTTP ${res.status} — owner may need manual reset via console`);
                return false;
            }
            return true;
        }
        catch (err) {
            logger.warn(`Onboarding service reset error: ${describeError(err)} — owner may need manual reset via console`);
            return false;
        }
    }
    /**
     * Report Google OAuth tokens to the server after runGoogleOAuth() completes.
     * The server stores the tokens and advances the profile's onboarding_phase
     * from google_activated -> google_ready.
     *
     * Non-fatal: a network failure only logs a warning and does not block setup.
     * The tokens are already on local disk at this point.
     */
    async reportGoogleTokens(oauthTokensJson) {
        try {
            const res = await fetch(`${this.baseUrl}/api/agents/${this.agentName}/google-tokens`, {
                method: "POST",
                headers: {
                    Authorization: `Bearer ${this.apiKey}`,
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({ oauthTokensJson }),
                signal: AbortSignal.timeout(30_000),
            });
            if (!res.ok) {
                logger.warn(`Failed to report Google tokens to onboarding service: HTTP ${res.status}`);
                return false;
            }
            return true;
        }
        catch (err) {
            logger.warn(`Failed to report Google tokens to onboarding service: ${describeError(err)}`);
            return false;
        }
    }
    async notifyEmailSent() {
        try {
            const res = await fetch(`${this.baseUrl}/api/agents/${this.agentName}/email-sent`, {
                method: "POST",
                headers: { Authorization: `Bearer ${this.apiKey}` },
                signal: AbortSignal.timeout(10_000),
            });
            if (!res.ok) {
                logger.warn(`Onboarding service email-sent notification failed: HTTP ${res.status}`);
            }
        }
        catch (err) {
            logger.warn(`Onboarding service email-sent error: ${describeError(err)}`);
        }
    }
    async sendUsageBackfill(usageReports) {
        try {
            const res = await fetch(`${this.baseUrl}/api/agents/${this.agentName}/usage/backfill`, {
                method: "POST",
                headers: {
                    Authorization: `Bearer ${this.apiKey}`,
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({ usageReports }),
                signal: AbortSignal.timeout(30_000),
            });
            if (!res.ok) {
                logger.warn(`Usage backfill failed: HTTP ${res.status}`);
                return false;
            }
            return true;
        }
        catch (err) {
            logger.warn(`Usage backfill error: ${describeError(err)}`);
            return false;
        }
    }
    async getUsagePeriods() {
        try {
            const res = await fetch(`${this.baseUrl}/api/agents/${this.agentName}/usage/periods`, {
                method: "GET",
                headers: { Authorization: `Bearer ${this.apiKey}` },
                signal: AbortSignal.timeout(15_000),
            });
            if (!res.ok) {
                logger.warn(`Usage periods lookup failed: HTTP ${res.status}`);
                return { success: false, periods: [] };
            }
            const payload = await res.json();
            return { success: true, periods: payload.periods ?? [] };
        }
        catch (err) {
            logger.warn(`Usage periods lookup error: ${describeError(err)}`);
            return { success: false, periods: [] };
        }
    }
    async downloadUsagePdf(localPath, period = "current") {
        try {
            const res = await fetch(`${this.baseUrl}/api/agents/${this.agentName}/usage/pdf?period=${period}`, {
                method: "GET",
                headers: { Authorization: `Bearer ${this.apiKey}` },
                signal: AbortSignal.timeout(30_000),
            });
            if (!res.ok) {
                logger.warn(`Usage PDF download failed: HTTP ${res.status}`);
                return { success: false, finalPath: localPath };
            }
            const periodStart = res.headers.get("X-Usage-Period-Start") ?? undefined;
            const periodEnd = res.headers.get("X-Usage-Period-End") ?? undefined;
            const arrayBuffer = await res.arrayBuffer();
            // Always write to the caller-chosen path. The caller is responsible
            // for choosing a cache key that uniquely identifies the period range.
            await writeFile(localPath, Buffer.from(arrayBuffer));
            return { success: true, finalPath: localPath, periodStart, periodEnd };
        }
        catch (err) {
            logger.warn(`Usage PDF download error: ${describeError(err)}`);
            return { success: false, finalPath: localPath };
        }
    }
    /**
     * Request a Stripe Checkout Session from the backend.
     * Returns the checkout URL for the owner to complete payment.
     */
    async createCheckoutSession(planId, ownerChatId) {
        try {
            const res = await fetch(`${this.baseUrl}/api/agents/${this.agentName}/checkout-session`, {
                method: "POST",
                headers: {
                    Authorization: `Bearer ${this.apiKey}`,
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({
                    planId,
                    ownerChatId: ownerChatId ? String(ownerChatId) : undefined,
                }),
                signal: AbortSignal.timeout(15_000),
            });
            if (!res.ok) {
                logger.warn(`Checkout session creation failed: HTTP ${res.status}`);
                return null;
            }
            return (await res.json());
        }
        catch (err) {
            logger.warn(`Checkout session error: ${describeError(err)}`);
            return null;
        }
    }
    /**
     * Request a one-time Stripe Checkout Session for a bundle purchase (top-up).
     * Returns the checkout URL for the owner to make a one-time payment.
     */
    async createBundlePurchase(amount, ownerChatId) {
        try {
            const res = await fetch(`${this.baseUrl}/api/agents/${this.agentName}/bundle-purchase`, {
                method: "POST",
                headers: {
                    Authorization: `Bearer ${this.apiKey}`,
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({
                    amount,
                    ownerChatId: ownerChatId ? String(ownerChatId) : undefined,
                }),
                signal: AbortSignal.timeout(15_000),
            });
            if (!res.ok) {
                logger.warn(`Bundle purchase creation failed: HTTP ${res.status}`);
                return null;
            }
            return (await res.json());
        }
        catch (err) {
            logger.warn(`Bundle purchase error: ${describeError(err)}`);
            return null;
        }
    }
    /**
     * Request a Stripe Billing Portal session from the backend.
     * Returns subscription info and a portal URL for the owner to manage billing.
     */
    async getBillingPortal() {
        try {
            const res = await fetch(`${this.baseUrl}/api/agents/${this.agentName}/billing-portal`, {
                method: "POST",
                headers: { Authorization: `Bearer ${this.apiKey}` },
                signal: AbortSignal.timeout(15_000),
            });
            if (!res.ok) {
                logger.warn(`Billing portal request failed: HTTP ${res.status}`);
                return null;
            }
            return (await res.json());
        }
        catch (err) {
            logger.warn(`Billing portal error: ${describeError(err)}`);
            return null;
        }
    }
    async notifyComplete(ownerInfo) {
        try {
            const res = await fetch(`${this.baseUrl}/api/agents/${this.agentName}/complete`, {
                method: "POST",
                headers: {
                    Authorization: `Bearer ${this.apiKey}`,
                    "Content-Type": "application/json",
                },
                body: JSON.stringify(ownerInfo ?? {}),
                signal: AbortSignal.timeout(10_000),
            });
            if (!res.ok) {
                logger.warn(`Onboarding service complete notification failed: HTTP ${res.status}`);
            }
        }
        catch (err) {
            logger.warn(`Onboarding service complete error: ${describeError(err)}`);
        }
    }
}
//# sourceMappingURL=onboarding-service-client.js.map