/**
 * Auth credential inspector — reads from auth-service (single source of truth).
 *
 * Falls back to local accounts.json only if needed for token redaction tests.
 * Auth-service is always authoritative; reading local files risks showing
 * phantom credentials that no longer exist in the live vault.
 *
 * Token security (T4): accessToken and refreshToken fields are NEVER read
 * into variables used in output. Only safe fields are touched.
 */
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import pc from 'picocolors';
import { effectiveState, pickCredential, nextCooldownReset } from '@olam/auth-logic';
import { AuthClient } from '@olam/core/src/auth/index.js';
import { printError } from '../output.js';
import { EXIT_AUTH_NEEDS_ATTENTION } from '../exit-codes.js';
// ----- file path (mirrors auth-service/src/store.ts) -------------------
const DOCKER_DATA_DIR = '/auth-data';
const LOCAL_DATA_DIR = path.join(os.homedir(), '.olam', 'auth-data');
function resolveAccountsPath() {
    if (fs.existsSync(DOCKER_DATA_DIR)) {
        return path.join(DOCKER_DATA_DIR, 'accounts.json');
    }
    return path.join(LOCAL_DATA_DIR, 'accounts.json');
}
export function readAccountsFile(filePath) {
    if (!fs.existsSync(filePath))
        return [];
    try {
        const raw = fs.readFileSync(filePath, 'utf-8');
        const parsed = JSON.parse(raw);
        if (!Array.isArray(parsed))
            return [];
        // Project only safe fields; accessToken / refreshToken are never extracted.
        return parsed.map((r) => ({
            id: String(r['id'] ?? ''),
            accountLabel: r['accountLabel'] != null ? String(r['accountLabel']) : undefined,
            expiresAt: typeof r['expiresAt'] === 'number' ? r['expiresAt'] : undefined,
            rateLimited: typeof r['rateLimited'] === 'boolean' ? r['rateLimited'] : undefined,
            rateLimitResetsAt: typeof r['rateLimitResetsAt'] === 'string' ? r['rateLimitResetsAt'] : undefined,
            lastUsed: typeof r['lastUsed'] === 'string' ? r['lastUsed'] : undefined,
            state: isCredentialState(r['state']) ? r['state'] : undefined,
            usage: r['usage'] != null && typeof r['usage'] === 'object'
                ? {
                    requestCount5h: typeof r['usage']['requestCount5h'] === 'number'
                        ? r['usage']['requestCount5h']
                        : 0,
                    last429At: typeof r['usage']['last429At'] === 'string'
                        ? r['usage']['last429At']
                        : null,
                }
                : undefined,
        }));
    }
    catch {
        return [];
    }
}
function isCredentialState(v) {
    return v === 'active' || v === 'cooldown' || v === 'expired' || v === 'disabled';
}
// ----- formatting helpers -----------------------------------------------
function localHHMM(isoStr) {
    const d = new Date(isoStr);
    return d.toLocaleTimeString(undefined, {
        hour: '2-digit',
        minute: '2-digit',
        hour12: false,
    });
}
function daysAgoStr(expiresAt, now) {
    const diffDays = Math.floor((now - expiresAt) / (1000 * 60 * 60 * 24));
    if (diffDays <= 0)
        return 'expired today';
    return `expired ${diffDays} day${diffDays === 1 ? '' : 's'} ago`;
}
function trunc(s, maxLen) {
    return s.length > maxLen ? s.slice(0, maxLen) : s;
}
// ----- table builder -----------------------------------------------------
const STATE_PRIORITY = {
    active: 0,
    cooldown: 1,
    'usage-capped': 2,
    disabled: 3,
    expired: 4,
};
export function formatAuthStatus(accounts, now = Date.now()) {
    const picked = pickCredential(accounts, now);
    const rows = accounts.map((account) => {
        const state = effectiveState(account, now);
        const isPicked = picked != null && account.id === picked.id;
        const req5h = account.usage?.requestCount5h ?? 0;
        const last429 = account.usage?.last429At ? localHHMM(account.usage.last429At) : 'never';
        let reason;
        if (isPicked) {
            reason = '← selected';
        }
        else if (state === 'active') {
            reason = `req5h=${req5h} (higher than candidate)`;
        }
        else if (state === 'cooldown') {
            const resetTime = account.rateLimitResetsAt ? localHHMM(account.rateLimitResetsAt) : '?';
            reason = `cooldown until ${resetTime}`;
        }
        else if (state === 'expired') {
            reason = daysAgoStr(account.expiresAt ?? 0, now);
        }
        else {
            reason = 'disabled';
        }
        return { id: account.id, label: account.accountLabel ?? account.id, state, reason, req5h, last429, isPicked };
    });
    rows.sort((a, b) => {
        if (a.isPicked !== b.isPicked)
            return a.isPicked ? -1 : 1;
        return STATE_PRIORITY[a.state] - STATE_PRIORITY[b.state];
    });
    const COL = { id: 17, label: 17, state: 11, reason: 28, req5h: 7 };
    const lines = [];
    const hdr = 'id'.padEnd(COL.id) +
        'label'.padEnd(COL.label) +
        'state'.padEnd(COL.state) +
        'reason'.padEnd(COL.reason) +
        'req5h'.padEnd(COL.req5h) +
        'last429';
    lines.push(pc.dim(hdr));
    lines.push(pc.dim('-'.repeat(hdr.length)));
    for (const row of rows) {
        const id = trunc(row.id, 16).padEnd(COL.id);
        const label = trunc(row.label, 16).padEnd(COL.label);
        const stateRaw = row.state.padEnd(COL.state);
        const stateColored = row.state === 'active'
            ? pc.green(stateRaw)
            : row.state === 'cooldown'
                ? pc.yellow(stateRaw)
                : row.state === 'expired'
                    ? pc.red(stateRaw)
                    : pc.dim(stateRaw);
        const reason = row.reason.padEnd(COL.reason);
        const req5h = String(row.req5h).padEnd(COL.req5h);
        if (row.isPicked) {
            lines.push(pc.bold(id) + pc.bold(label) + stateColored + pc.green(reason) + pc.dim(req5h) + pc.dim(row.last429));
        }
        else {
            lines.push(id + label + stateColored + reason + pc.dim(req5h) + pc.dim(row.last429));
        }
    }
    if (picked == null) {
        const resetIso = nextCooldownReset(accounts, now);
        lines.push('');
        lines.push(resetIso
            ? pc.yellow(`Next reset: ${localHHMM(resetIso)}`)
            : pc.dim('No reset scheduled'));
        return { output: lines.join('\n'), exitCode: EXIT_AUTH_NEEDS_ATTENTION };
    }
    return { output: lines.join('\n'), exitCode: 0 };
}
// ----- CLI entry point ---------------------------------------------------
/** Projects auth-service account summary to safe local format for formatting. */
function toSafeAccount(a) {
    return {
        id: a.id,
        accountLabel: a.accountLabel,
        // expiresAt not exposed in summary — state is pre-computed server-side
        rateLimited: a.rateLimited,
        rateLimitResetsAt: a.rateLimitResetsAt,
        lastUsed: a.lastUsed,
        state: a.state,
        usage: a.usage
            ? { requestCount5h: a.usage.requestCount5h, last429At: a.usage.last429At }
            : undefined,
    };
}
/**
 * Run `olam auth status`.
 *
 * @param getStatus - Injectable status fetcher for tests; defaults to AuthClient.status().
 */
export async function runAuthStatus(getStatus) {
    const fetchStatus = getStatus ?? (() => new AuthClient().status());
    let status;
    try {
        status = await fetchStatus();
    }
    catch {
        printError('Failed to contact auth service. Run `olam auth up` first.');
        process.exitCode = 1;
        return;
    }
    if (!status.reachable) {
        printError('Auth container is not reachable. Run `olam auth up` first.');
        process.exitCode = 1;
        return;
    }
    if (status.accounts.length === 0) {
        console.log(pc.dim('No credentials found. Run: olam auth login'));
        return;
    }
    const accounts = status.accounts.map(toSafeAccount);
    const result = formatAuthStatus(accounts);
    console.log(result.output);
    if (result.exitCode !== 0) {
        process.exitCode = result.exitCode;
    }
}
//# sourceMappingURL=auth-status.js.map