"use strict";
/**
 * test_license — covers the open-core licensing core end-to-end without any
 * network or production key: a throwaway Ed25519 keypair signs tokens, and we
 * assert verify / expiry / parsing / persistence / entitlements / CLI states.
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
const crypto = __importStar(require("crypto"));
const fs = __importStar(require("fs"));
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const LicenseManager_1 = require("./orchestrator/LicenseManager");
const LicenseStore_1 = require("./orchestrator/LicenseStore");
const Entitlements_1 = require("./orchestrator/Entitlements");
const LicenseCommand_1 = require("./cli/LicenseCommand");
function assert(cond, msg) {
    if (!cond)
        throw new Error(`ASSERT_FAILED: ${msg}`);
}
// --- throwaway signing keypair (stands in for the private license server) ---
const { publicKey, privateKey } = crypto.generateKeyPairSync('ed25519');
const PUBLIC_PEM = publicKey.export({ type: 'spki', format: 'pem' }).toString();
function sign(payload) {
    const sig = crypto.sign(null, Buffer.from(LicenseManager_1.LicenseManager.canonicalize(payload)), privateKey);
    return { payload, signature: sig.toString('base64') };
}
function payload(over = {}) {
    const now = Date.now();
    return {
        keyId: 'PRO-AB12-CD34-EF56-GH78',
        tier: 'pro',
        customerId: 'cust_test',
        issuedAt: new Date(now).toISOString(),
        expiresAt: new Date(now + 30 * 86_400_000).toISOString(),
        ...over,
    };
}
const manager = new LicenseManager_1.LicenseManager(PUBLIC_PEM);
// 1) key-id parsing
assert(manager.parseKeyId('PRO-AB12-CD34-EF56-GH78')?.tier === 'pro', 'parse pro');
assert(manager.parseKeyId('team-ab12-cd34-ef56-gh78')?.tier === 'team', 'parse team lowercased');
assert(manager.parseKeyId('ENT-AB12-CD34-EF56-GH78')?.tier === 'enterprise', 'parse ent');
assert(manager.parseKeyId('PRO-AB12-CD34-EF56') === null, 'reject too short');
assert(manager.parseKeyId('FOO-AB12-CD34-EF56-GH78') === null, 'reject bad prefix');
// 2) valid token verifies and is current
const good = sign(payload());
assert(manager.verifyToken(good).valid, 'valid token verifies');
assert(manager.isCurrent(good), 'valid token is current');
assert(manager.entitlements(good).includes('hosted-dashboard'), 'pro entitlements resolved');
// 3) forged token (tampered payload after signing) is rejected
const forged = { payload: { ...good.payload, tier: 'enterprise' }, signature: good.signature };
assert(!manager.verifyToken(forged).valid, 'tampered tier rejected');
// 4) wrong-key signature rejected (signed by a different keypair)
const { privateKey: otherPriv } = crypto.generateKeyPairSync('ed25519');
const p = payload();
const wrongSig = crypto.sign(null, Buffer.from(LicenseManager_1.LicenseManager.canonicalize(p)), otherPriv).toString('base64');
assert(!manager.verifyToken({ payload: p, signature: wrongSig }).valid, 'foreign signature rejected');
// 5) expired token verifies (signature ok) but is not current
const expired = sign(payload({ expiresAt: new Date(Date.now() - 86_400_000).toISOString() }));
assert(manager.verifyToken(expired).valid, 'expired token still has valid signature');
assert(!manager.isCurrent(expired), 'expired token not current');
// 6) tier-prefix coherence: ENT key claiming pro tier is rejected
const mismatch = sign(payload({ keyId: 'ENT-AB12-CD34-EF56-GH78', tier: 'pro' }));
assert(manager.verifyToken(mismatch).reason === 'tier-prefix-mismatch', 'tier/prefix mismatch caught');
// --- persistence + entitlements in an isolated temp root ---
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'openlife-license-'));
try {
    const store = new LicenseStore_1.LicenseStore(root);
    assert(store.current() === null, 'store empty initially');
    store.save(good, 'activate');
    const reread = store.current();
    assert(reread?.payload.keyId === good.payload.keyId, 'store round-trip');
    assert(store.events().some((e) => e.type === 'activate'), 'activate event logged');
    const ent = new Entitlements_1.Entitlements(root, manager);
    assert(ent.state().status === 'active', 'entitlements active');
    assert(ent.hasFeature('hosted-dashboard'), 'has pro feature');
    assert(!ent.hasFeature('sso-saml'), 'pro lacks enterprise feature');
    let threw = false;
    try {
        ent.requireFeature('sso-saml');
    }
    catch {
        threw = true;
    }
    assert(threw, 'requireFeature throws for missing feature');
    // CLI status reflects active
    const cmd = new LicenseCommand_1.LicenseCommand(root, manager);
    const st = cmd.status();
    assert(st.status === 'active' && st.tier === 'pro', 'CLI status active pro');
    assert((st.daysRemaining ?? 0) > 0, 'days remaining positive');
    // deactivate reverts to community
    cmd.deactivate();
    assert(new Entitlements_1.Entitlements(root, manager).state().status === 'none', 'deactivate -> none');
    assert(store.events().some((e) => e.type === 'deactivate'), 'deactivate event logged');
    // CLI status with no license = community, ok:true, no features gated
    const community = cmd.status();
    assert(community.status === 'none' && community.ok && community.tier === 'community', 'community status ok');
    assert((community.features ?? []).length === 0, 'community grants nothing');
    // activate from token file (offline path)
    const tokenFile = path.join(root, 'token.json');
    fs.writeFileSync(tokenFile, JSON.stringify(sign(payload({ keyId: 'TEAM-AB12-CD34-EF56-GH78', tier: 'team' }))));
    const fileRes = cmd.activateFromTokenFile(tokenFile);
    assert(fileRes.ok && fileRes.tier === 'team', 'activate from token file');
    assert((fileRes.features ?? []).includes('team-workspace'), 'team feature granted via file');
    // rejecting an expired token file
    const expFile = path.join(root, 'expired.json');
    fs.writeFileSync(expFile, JSON.stringify(expired));
    assert(!cmd.activateFromTokenFile(expFile).ok, 'expired token file rejected');
}
finally {
    fs.rmSync(root, { recursive: true, force: true });
}
console.log('TEST_LICENSE_OK');
