/**
 * JWT Dev Command
 * Mint a local development JWT for quick manual API testing.
 */
import { isUndefinedOrNull } from '../../helper/index.js';
import { BaseCommand } from '../BaseCommand.js';
import { appConfig } from '../../config/app.js';
import { securityConfig } from '../../config/security.js';
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
import * as crypto from '../../node-singletons/crypto.js';
import { JwtManager } from '../../security/JwtManager.js';
const sha256Hex = (value) => {
    return crypto.createHash('sha256').update(value).digest('hex');
};
const optionalTrimmed = (value) => {
    if (typeof value !== 'string')
        return undefined;
    const trimmed = value.trim();
    return trimmed === '' ? undefined : trimmed;
};
const buildPayload = (options) => {
    const payload = {};
    const sub = optionalTrimmed(options.sub);
    if (!isUndefinedOrNull(sub))
        payload.sub = sub;
    const email = optionalTrimmed(options.email);
    if (!isUndefinedOrNull(email))
        payload['email'] = email;
    const role = optionalTrimmed(options.role);
    if (!isUndefinedOrNull(role))
        payload['role'] = role;
    const deviceId = optionalTrimmed(options.deviceId);
    if (!isUndefinedOrNull(deviceId))
        payload['deviceId'] = deviceId;
    const tenantId = optionalTrimmed(options.tenantId);
    if (!isUndefinedOrNull(tenantId))
        payload['tenantId'] = tenantId;
    const tz = optionalTrimmed(options.tz);
    if (!isUndefinedOrNull(tz))
        payload['tz'] = tz;
    const uaHash = optionalTrimmed(options.uaHash);
    if (isUndefinedOrNull(uaHash)) {
        const ua = optionalTrimmed(options.ua);
        if (ua !== undefined) {
            payload['uaHash'] = sha256Hex(ua);
        }
    }
    else {
        payload['uaHash'] = uaHash;
    }
    return payload;
};
const parseExpiresToSeconds = (value) => {
    const raw = typeof value === 'string' ? value.trim() : '';
    if (raw === '')
        return 60 * 60;
    // Allow raw seconds
    if (/^\d+$/.test(raw)) {
        const seconds = Number.parseInt(raw, 10);
        if (!Number.isFinite(seconds) || seconds <= 0) {
            throw ErrorFactory.createCliError(`Invalid --expires '${raw}'. Must be > 0 seconds.`);
        }
        return seconds;
    }
    const match = /^(\d+)([smhd])$/i.exec(raw);
    if (!match) {
        throw ErrorFactory.createCliError(`Invalid --expires '${raw}'. Use seconds (e.g. 3600) or a duration like 30m, 1h, 7d.`);
    }
    const amount = Number.parseInt(match[1] ?? '', 10);
    const unit = (match[2] ?? '').toLowerCase();
    if (!Number.isFinite(amount) || amount <= 0) {
        throw ErrorFactory.createCliError(`Invalid --expires '${raw}'. Must be > 0.`);
    }
    const multipliers = {
        s: 1,
        m: 60,
        h: 60 * 60,
        d: 24 * 60 * 60,
    };
    const multiplier = multipliers[unit];
    if (multiplier === undefined) {
        throw ErrorFactory.createCliError(`Invalid --expires '${raw}'. Supported units: s, m, h, d (e.g. 30m, 1h).`);
    }
    return amount * multiplier;
};
const assertNotProduction = (allowProduction) => {
    if (!appConfig.isProduction())
        return;
    if (allowProduction === true)
        return;
    throw ErrorFactory.createCliError("Refusing to mint a dev JWT in production. Use --allow-production only if you know what you're doing.");
};
export const JwtDevCommand = Object.freeze(BaseCommand.create({
    name: 'jwt:dev',
    description: 'Mint a local development JWT (for manual API testing)',
    aliases: ['jwt:token', 'jwt:mint'],
    addOptions: (command) => {
        command
            .option('--sub <sub>', 'JWT subject claim (default: 1)', '1')
            .option('--email <email>', 'Email claim')
            .option('--role <role>', 'Role claim')
            .option('--device-id <id>', 'Attach deviceId claim (for bulletproof auth)')
            .option('--tenant-id <id>', 'Attach tenantId claim')
            .option('--tz <tz>', 'Attach timezone claim (tz)')
            .option('--ua <ua>', 'Compute and attach uaHash claim from a User-Agent string')
            .option('--ua-hash <hash>', 'Attach uaHash claim directly (hex)')
            .option('--expires <duration>', "Expiry: seconds or 30m/1h/7d (default: '1h')", '1h')
            .option('--json', 'Output machine-readable JSON')
            .option('--allow-production', 'Allow running in production (dangerous)');
    },
    execute: async (options) => {
        assertNotProduction(options.allowProduction);
        const expiresInSeconds = parseExpiresToSeconds(options.expires);
        const payload = buildPayload(options);
        const token = await JwtManager.signAccessToken(payload, expiresInSeconds);
        /* eslint-disable no-console */
        if (options.json === true) {
            const nowSeconds = Math.floor(Date.now() / 1000);
            console.log(JSON.stringify({
                token,
                algorithm: securityConfig.jwt.algorithm,
                expiresIn: expiresInSeconds,
                issuedAt: nowSeconds,
                expiresAt: nowSeconds + expiresInSeconds,
                payload,
            }));
            return;
        }
        console.log(token);
        /* eslint-enable no-console */
    },
}));
export default JwtDevCommand;
