import { env } from '@4runr/shared';
import crypto from 'crypto';
export const DEFAULT_DEV_SIGNING_SECRET = 'default-secret-change-in-production';
export const MIN_TOKEN_TTL_HOURS = 1;
export const MAX_TOKEN_TTL_HOURS = 168;
const INSECURE_SECRET_SUBSTRINGS = [
    'secret',
    'change-me',
    'change-in-production',
    'test-secret',
    'default-dev',
    'jwt-secret',
];
export function looksInsecureSecret(value) {
    const lower = value.toLowerCase();
    return INSECURE_SECRET_SUBSTRINGS.some((p) => lower.includes(p));
}
export function signingSecretProductionError(secret) {
    if (!secret || secret.length < 32) {
        return 'SIGNING_SECRET must be set to at least 32 characters in production (Covenant / agent tokens)';
    }
    if (secret === DEFAULT_DEV_SIGNING_SECRET || looksInsecureSecret(secret)) {
        return 'SIGNING_SECRET must not use a default or obvious placeholder in production';
    }
    return null;
}
function isProductionNode() {
    return (process.env['NODE_ENV'] ?? env.NODE_ENV) === 'production';
}
export function getSigningSecret() {
    const s = process.env['SIGNING_SECRET'];
    if (isProductionNode()) {
        const err = signingSecretProductionError(s);
        if (err) {
            return { secret: '', error: err };
        }
        return { secret: s };
    }
    return { secret: s && s.length >= 32 ? s : DEFAULT_DEV_SIGNING_SECRET };
}
function hmacSignaturesMatch(providedHex, expectedHex) {
    if (typeof providedHex !== 'string' || typeof expectedHex !== 'string') {
        return false;
    }
    const provided = Buffer.from(providedHex, 'hex');
    const expected = Buffer.from(expectedHex, 'hex');
    if (provided.length !== expected.length || provided.length === 0) {
        return false;
    }
    return crypto.timingSafeEqual(provided, expected);
}
function parseTokenExpiry(expiresAtRaw) {
    const expiresAt = new Date(expiresAtRaw);
    if (Number.isNaN(expiresAt.getTime())) {
        return null;
    }
    return expiresAt;
}
export function validateMintRequest(body) {
    const agent_id = body.agent_id;
    if (typeof agent_id !== 'string' || !agent_id.trim()) {
        return { ok: false, error: 'agent_id must be a non-empty string' };
    }
    if (!isNonEmptyStringArray(body.tools)) {
        return {
            ok: false,
            error: 'tools must be a non-empty array of non-empty strings',
            details: { minTools: 1 },
        };
    }
    if (!isStringArray(body.permissions)) {
        return {
            ok: false,
            error: 'permissions must be an array of strings',
        };
    }
    const rawHours = body.expiresInHours ?? 24;
    if (typeof rawHours !== 'number' || !Number.isFinite(rawHours)) {
        return {
            ok: false,
            error: 'expiresInHours must be a number',
        };
    }
    const expiresInHours = Math.floor(rawHours);
    if (expiresInHours < MIN_TOKEN_TTL_HOURS || expiresInHours > MAX_TOKEN_TTL_HOURS) {
        return {
            ok: false,
            error: `expiresInHours must be between ${MIN_TOKEN_TTL_HOURS} and ${MAX_TOKEN_TTL_HOURS}`,
            details: { min: MIN_TOKEN_TTL_HOURS, max: MAX_TOKEN_TTL_HOURS },
        };
    }
    return {
        ok: true,
        data: {
            agent_id: agent_id.trim(),
            tools: body.tools.map((t) => t.trim()),
            permissions: body.permissions.map((p) => String(p).trim()),
            expiresInHours,
        },
    };
}
function isNonEmptyStringArray(val) {
    return (Array.isArray(val) &&
        val.length > 0 &&
        val.every((x) => typeof x === 'string' && x.trim().length > 0));
}
function isStringArray(val) {
    return Array.isArray(val) && val.every((x) => typeof x === 'string');
}
export function signAgentToken(payload, secretOverride) {
    const { secret: fromEnv, error: secretError } = getSigningSecret();
    const secret = secretOverride ?? fromEnv;
    if (!secret) {
        return { error: secretError || 'Signing is not configured' };
    }
    const encryptedPayload = Buffer.from(JSON.stringify(payload)).toString('base64');
    const signature = crypto
        .createHmac('sha256', secret)
        .update(encryptedPayload)
        .digest('hex');
    return { token: `${encryptedPayload}.${signature}` };
}
export function verifyAgentToken(agentToken) {
    const { secret, error: secretError } = getSigningSecret();
    if (!secret) {
        return { valid: false, error: secretError || 'Signing is not configured' };
    }
    try {
        const [encryptedPayload, signature] = agentToken.split('.');
        if (!encryptedPayload || !signature) {
            return { valid: false, error: 'Invalid token format' };
        }
        const validSignature = crypto
            .createHmac('sha256', secret)
            .update(encryptedPayload)
            .digest('hex');
        if (!hmacSignaturesMatch(signature, validSignature)) {
            return { valid: false, error: 'Invalid signature' };
        }
        const payload = Buffer.from(encryptedPayload, 'base64').toString('utf8');
        const tokenData = JSON.parse(payload);
        const expiresAt = parseTokenExpiry(tokenData.expires_at);
        if (!expiresAt) {
            return { valid: false, error: 'Invalid token expiry' };
        }
        if (expiresAt.getTime() <= Date.now()) {
            return { valid: false, error: 'Token expired' };
        }
        return { valid: true, data: tokenData };
    }
    catch {
        return { valid: false, error: 'Token verification failed' };
    }
}
export function assertToolAllowed(tokenData, tool) {
    if (!tokenData.tools.includes(tool)) {
        return { allowed: false, allowedTools: tokenData.tools };
    }
    return { allowed: true };
}
//# sourceMappingURL=agent-token.js.map