import * as SecureStore from "expo-secure-store"; import type { ClaudeUsageResponse } from "@/types/claude"; import type { CodexUsageResponse } from "@/types/codex"; const KEYS = { CODEX_AUTH: "codexbar_codex_auth", CLAUDE_SESSION_KEY: "codexbar_claude_session_key", CLAUDE_LAST_ACTIVE_ORG: "codexbar_claude_last_active_org", CODEX_USAGE_CACHE: "codexbar_codex_usage_cache", CLAUDE_USAGE_CACHE: "codexbar_claude_usage_cache", NOTIF_DAILY_ENABLED: "notifDailyEnabled", NOTIF_DAILY_HOUR: "notifDailyHour", NOTIF_DAILY_MINUTE: "notifDailyMinute", NOTIF_THRESHOLD_ENABLED: "notifThresholdEnabled", NOTIF_THRESHOLD_PCT: "notifThresholdPct", NOTIF_THRESHOLD_LAST_FIRED: "notifThresholdLastFired", } as const; export interface UsageCache { data: T; lastFetchedAt: number; } async function loadJson(key: string): Promise { const raw = await SecureStore.getItemAsync(key); if (!raw) return null; try { return JSON.parse(raw) as T; } catch { await SecureStore.deleteItemAsync(key); return null; } } async function saveJson(key: string, value: unknown): Promise { await SecureStore.setItemAsync(key, JSON.stringify(value)); } export interface NotifSettings { dailyEnabled: boolean; dailyHour: number; dailyMinute: number; thresholdEnabled: boolean; /** Alert when remaining quota falls below this % (e.g. 20 = fire when < 20% left) */ thresholdPct: number; } export async function loadNotifSettings(): Promise { const [de, dh, dm, te, tp] = await Promise.all([ SecureStore.getItemAsync(KEYS.NOTIF_DAILY_ENABLED), SecureStore.getItemAsync(KEYS.NOTIF_DAILY_HOUR), SecureStore.getItemAsync(KEYS.NOTIF_DAILY_MINUTE), SecureStore.getItemAsync(KEYS.NOTIF_THRESHOLD_ENABLED), SecureStore.getItemAsync(KEYS.NOTIF_THRESHOLD_PCT), ]); return { dailyEnabled: de === "true", dailyHour: dh !== null ? parseInt(dh, 10) : 9, dailyMinute: dm !== null ? parseInt(dm, 10) : 0, thresholdEnabled: te === "true", thresholdPct: tp !== null ? parseInt(tp, 10) : 20, }; } export async function saveNotifSettings(s: NotifSettings): Promise { await Promise.all([ SecureStore.setItemAsync(KEYS.NOTIF_DAILY_ENABLED, String(s.dailyEnabled)), SecureStore.setItemAsync(KEYS.NOTIF_DAILY_HOUR, String(s.dailyHour)), SecureStore.setItemAsync(KEYS.NOTIF_DAILY_MINUTE, String(s.dailyMinute)), SecureStore.setItemAsync(KEYS.NOTIF_THRESHOLD_ENABLED, String(s.thresholdEnabled)), SecureStore.setItemAsync(KEYS.NOTIF_THRESHOLD_PCT, String(s.thresholdPct)), ]); } export async function loadNotifThresholdLastFired(): Promise { return SecureStore.getItemAsync(KEYS.NOTIF_THRESHOLD_LAST_FIRED); } export async function saveNotifThresholdLastFired(date: string): Promise { await SecureStore.setItemAsync(KEYS.NOTIF_THRESHOLD_LAST_FIRED, date); } export async function saveCodexAuth(auth: { accessToken: string; accountId?: string; }): Promise { await saveJson(KEYS.CODEX_AUTH, auth); } export async function loadCodexAuth(): Promise<{ accessToken: string; accountId?: string; } | null> { return loadJson(KEYS.CODEX_AUTH); } export function loadCodexUsageCache(): Promise | null> { return loadJson(KEYS.CODEX_USAGE_CACHE); } export function saveCodexUsageCache( cache: UsageCache ): Promise { return saveJson(KEYS.CODEX_USAGE_CACHE, cache); } export function clearCodexUsageCache(): Promise { return SecureStore.deleteItemAsync(KEYS.CODEX_USAGE_CACHE); } export function loadClaudeUsageCache(): Promise | null> { return loadJson(KEYS.CLAUDE_USAGE_CACHE); } export function saveClaudeUsageCache( cache: UsageCache ): Promise { return saveJson(KEYS.CLAUDE_USAGE_CACHE, cache); } export function clearClaudeUsageCache(): Promise { return SecureStore.deleteItemAsync(KEYS.CLAUDE_USAGE_CACHE); } export async function saveClaudeSessionKey(key: string): Promise { await SecureStore.setItemAsync(KEYS.CLAUDE_SESSION_KEY, key.trim()); } export async function loadClaudeSessionKey(): Promise { return SecureStore.getItemAsync(KEYS.CLAUDE_SESSION_KEY); } export async function saveClaudeLastActiveOrg(orgId: string): Promise { await SecureStore.setItemAsync(KEYS.CLAUDE_LAST_ACTIVE_ORG, orgId.trim()); } export async function loadClaudeLastActiveOrg(): Promise { return SecureStore.getItemAsync(KEYS.CLAUDE_LAST_ACTIVE_ORG); } export async function clearClaudeLastActiveOrg(): Promise { await SecureStore.deleteItemAsync(KEYS.CLAUDE_LAST_ACTIVE_ORG); } export async function clearCodexAuth(): Promise { await Promise.all([ SecureStore.deleteItemAsync(KEYS.CODEX_AUTH), clearCodexUsageCache(), ]); } export async function clearClaudeSessionKey(): Promise { await Promise.all([ SecureStore.deleteItemAsync(KEYS.CLAUDE_SESSION_KEY), SecureStore.deleteItemAsync(KEYS.CLAUDE_LAST_ACTIVE_ORG), clearClaudeUsageCache(), ]); }