import { createLogger } from "../../utils/logger/index.js";
import { init_logger } from "../../utils/logger.js";
//#region src/providers/auth-runtime/oauth.ts
init_logger();
const log = createLogger("AuthOAuthRefresh");
/** Number of ms before {@link AuthProfile.expiresAt} we treat the token as stale. */
const REFRESH_SKEW_MS = 6e4;
function isOAuthAccessTokenExpired(profile, nowMs) {
	if (profile.mode !== "oauth") return false;
	if (typeof profile.expiresAt !== "number" || profile.expiresAt <= 0) return false;
	const now = nowMs ?? Date.now();
	return profile.expiresAt - REFRESH_SKEW_MS <= now;
}
/**
* Refresh an OAuth access token using the standard refresh-token grant. The
* vendor must have set `oauthTokenEndpoint` and `oauthRefreshToken` on the
* profile when it was first persisted.
*
* Returns a new {@link AuthProfile} object — does NOT mutate the input. The
* caller is responsible for `store.save?.(returned)`.
*/
async function refreshOAuthProfile(profile, options = {}) {
	if (profile.mode !== "oauth") throw new Error(`refreshOAuthProfile: profile mode is "${profile.mode}", expected "oauth"`);
	if (!profile.oauthRefreshToken) throw new Error("refreshOAuthProfile: profile is missing oauthRefreshToken");
	if (!profile.oauthTokenEndpoint) throw new Error("refreshOAuthProfile: profile is missing oauthTokenEndpoint");
	const body = new URLSearchParams();
	body.set("grant_type", "refresh_token");
	body.set("refresh_token", profile.oauthRefreshToken);
	if (profile.oauthClientId) body.set("client_id", profile.oauthClientId);
	const fetcher = options.fetcher ?? fetch;
	const ctrl = new AbortController();
	const timer = setTimeout(() => ctrl.abort(), options.timeoutMs ?? 3e4);
	try {
		const res = await fetcher(profile.oauthTokenEndpoint, {
			method: "POST",
			headers: {
				"Content-Type": "application/x-www-form-urlencoded",
				Accept: "application/json"
			},
			body: body.toString(),
			signal: ctrl.signal
		});
		if (!res.ok) {
			const text = await safeReadText(res);
			throw new Error(`OAuth refresh failed: ${res.status} ${res.statusText}${text ? ` — ${truncate(text, 240)}` : ""}`);
		}
		const json = await res.json();
		const accessToken = pickString(json, "access_token");
		if (!accessToken) throw new Error("OAuth refresh response missing access_token");
		const refreshToken = pickString(json, "refresh_token") ?? profile.oauthRefreshToken;
		const expiresInSec = pickNumber(json, "expires_in");
		const expiresAt = expiresInSec ? Date.now() + expiresInSec * 1e3 : profile.expiresAt;
		return {
			...profile,
			oauthAccessToken: accessToken,
			oauthRefreshToken: refreshToken,
			expiresAt
		};
	} catch (err) {
		log.warn({
			err,
			provider: profile.provider,
			profileId: profile.profileId,
			phase: "refresh"
		}, `OAuth refresh failed for ${profile.provider}/${profile.profileId}`);
		throw err;
	} finally {
		clearTimeout(timer);
	}
}
function pickString(obj, key) {
	const v = obj[key];
	return typeof v === "string" && v.length > 0 ? v : void 0;
}
function pickNumber(obj, key) {
	const v = obj[key];
	return typeof v === "number" && Number.isFinite(v) ? v : void 0;
}
async function safeReadText(res) {
	try {
		return await res.text();
	} catch {
		return "";
	}
}
function truncate(s, max) {
	return s.length > max ? `${s.slice(0, max)}…` : s;
}
//#endregion
export { isOAuthAccessTokenExpired, refreshOAuthProfile };

//# sourceMappingURL=oauth.js.map