import { emptyUsage } from './sum-usage';
const STORAGE_PREFIX = 'genesis-ai-assistant:cost-history:';
/**
 * Every bucket must be a number before a row is trusted. Checked per field rather than
 * just testing for an object, because a partially-shaped `usage` would sum as `NaN` and
 * silently poison every total on the Usage tab.
 */
function isAggregateUsage(value) {
    if (typeof value !== 'object' || value == null)
        return false;
    const u = value;
    return (typeof u.costUsd === 'number' &&
        typeof u.uncachedInputTokens === 'number' &&
        typeof u.cacheReadTokens === 'number' &&
        typeof u.cacheWriteTokens === 'number' &&
        typeof u.outputTokens === 'number');
}
/** localStorage key for a host-scoped cost history list. */
export function costHistoryStorageKey(scope) {
    return `${STORAGE_PREFIX}${scope}`;
}
/** Load persisted cost sessions for a scope (newest first). */
export function loadCostSessionHistory(scope) {
    if (typeof localStorage === 'undefined')
        return [];
    try {
        const raw = localStorage.getItem(costHistoryStorageKey(scope));
        if (!raw)
            return [];
        const parsed = JSON.parse(raw);
        if (!Array.isArray(parsed))
            return [];
        return parsed.filter(isCostSessionRecord);
    }
    catch (_a) {
        return [];
    }
}
/** Replace the persisted list for a scope. */
export function saveCostSessionHistory(scope, records) {
    if (typeof localStorage === 'undefined')
        return;
    try {
        localStorage.setItem(costHistoryStorageKey(scope), JSON.stringify(records));
    }
    catch (_a) {
        // Quota exceeded or private browsing — non-fatal.
    }
}
/**
 * Pure upsert — `records` with `record` replacing any row sharing its `projectKey`, or
 * prepended when there is none.
 *
 * An upsert, not an append, because a row stands for a **project's lifetime usage** rather
 * than a visit to it. Appending cut a fresh row per page-hide, so one project accumulated
 * a stack of near-identical rows and its spend was counted once per refresh by anything
 * summing the list. Replacing in place also keeps the row's position stable, so the list
 * doesn't reshuffle while the user is working.
 *
 * Storage-agnostic on purpose: the same merge serves the synchronous `localStorage` path
 * and the async provider-backed one, so the two can never drift on what "upsert" means.
 * The caller owns persistence — see the assistant's `persistCostHistory`.
 */
export function upsertRecord(records, record) {
    const at = records.findIndex((r) => r.projectKey === record.projectKey);
    return at === -1 ? [record, ...records] : records.map((r, i) => (i === at ? record : r));
}
/**
 * The spend a project's next row must carry over from before its current transcript.
 *
 * `transcriptIsAuthoritative` means the open transcript already accounts for this project's
 * history — true exactly when a session snapshot is restored on load. The two branches are
 * not interchangeable, and picking the wrong one is silently destructive:
 *
 * - Authoritative: carry the previous row's own `banked` forward. Taking `usage` instead would
 *   re-add the restored history on every reload, the compounding double-count this replaced.
 * - Not authoritative: the whole previous total becomes banked, because nothing in the fresh
 *   transcript accounts for it. Taking `banked` instead would rewrite the row down to the
 *   current page-load and drop the project's earlier spend.
 *
 * Note both branches are idempotent — the result never includes the current transcript — which
 * is what lets two assistant instances sharing a session write the same row.
 */
export function resolveBankedUsage(existing, transcriptIsAuthoritative) {
    if (!existing)
        return emptyUsage();
    const carried = transcriptIsAuthoritative ? existing.banked : existing.usage;
    return carried !== null && carried !== void 0 ? carried : emptyUsage();
}
/** Clear all persisted records for a scope. */
export function clearCostSessionHistory(scope) {
    if (typeof localStorage === 'undefined')
        return;
    try {
        localStorage.removeItem(costHistoryStorageKey(scope));
    }
    catch (_a) {
        // Non-fatal.
    }
}
/** Display date for a history row (e.g. "Jun 14, 2026"). */
export function formatCostSessionDate(iso) {
    return new Intl.DateTimeFormat('en-US', {
        month: 'short',
        day: 'numeric',
        year: 'numeric',
    }).format(new Date(iso));
}
function isCostSessionRecord(value) {
    if (typeof value !== 'object' || value == null)
        return false;
    const r = value;
    return (typeof r.id === 'string' &&
        // Rows written before the upsert model have no `projectKey` and cannot be keyed
        // to a project, so they are discarded on load rather than silently pooled with
        // the new per-project rows (where they would double-count that project).
        typeof r.projectKey === 'string' &&
        typeof r.title === 'string' &&
        typeof r.updatedAt === 'string' &&
        isAggregateUsage(r.usage) &&
        // Absent is valid (nothing banked / pre-dates the field); present must be whole, since
        // a half-shaped `banked` would poison the lifetime total exactly as a bad `usage` would.
        (r.banked === undefined || isAggregateUsage(r.banked)) &&
        Array.isArray(r.models));
}
