"use strict";
/**
 * LicenseManager — pure validation logic for OpenLife Business license keys.
 *
 * Open-core boundary: this file ships in the PUBLIC repo and only ever
 * *verifies* licenses. It holds the Ed25519 PUBLIC key (safe to expose). The
 * matching PRIVATE key — the one that can *issue/sign* tokens — lives only in
 * the private `openlife-enterprise` license server and is never committed here.
 * Because forging a token requires the private key, publishing this code does
 * not enable piracy.
 *
 * Two artifacts (the short key the user types is NOT the signed token):
 *   1. Key ID      — `TIER-XXXX-XXXX-XXXX-XXXX`, human-friendly, pasted by the
 *                    user. Prefix selects the tier (PRO / TEAM / ENT).
 *   2. Signed token — `{ payload, signature }`, fetched once from the license
 *                    server during `activate`, then cached and re-verified
 *                    OFFLINE on every `status`. The `expiresAt` field is what
 *                    keeps the key "em dia": it must be refreshed online while
 *                    the subscription is active, and revoking the subscription
 *                    stops the refresh so the cached token eventually expires.
 *
 * No I/O here — persistence lives in LicenseStore, CLI wiring in LicenseCommand.
 */
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 });
exports.LicenseManager = exports.TIER_FEATURES = void 0;
const crypto = __importStar(require("crypto"));
/** Map the key-id prefix (`PRO`/`TEAM`/`ENT`) to a tier. */
const PREFIX_TIER = {
    PRO: 'pro',
    TEAM: 'team',
    ENT: 'enterprise',
};
/** Key-id shape: `PRO-AB12-CD34-EF56-GH78` (prefix + four 4-char groups). */
const KEY_ID_RE = /^(PRO|TEAM|ENT)-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/;
/**
 * Dev (NON-production) Ed25519 public key. Replace before launch with the key
 * whose private half lives in the private license server. Override at runtime
 * with `OPENLIFE_LICENSE_PUBLIC_KEY` (PEM string), which tests use to inject
 * their own throwaway keypair.
 */
const EMBEDDED_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAT+rXlFVMWHOVEEqaN7OBwIF/pDqTJdW+j0gEY4rjVOo=
-----END PUBLIC KEY-----`;
/**
 * Tier → feature entitlements.
 *
 * ⚠️ PRODUCT DECISION (yours, Rafael): this map is the contract for what each
 * paid tier unlocks. The values here are feature *names* the private repo's
 * paid code checks via `Entitlements.requireFeature(...)`. Nothing in the
 * open-source core is gated by this — Community ([]) stays fully functional.
 *
 * Higher tiers are cumulative (team includes pro, enterprise includes team).
 * Edit freely as the product takes shape.
 */
exports.TIER_FEATURES = {
    community: [],
    pro: [
        'hosted-dashboard',
        'device-sync',
        'extended-storage',
        'premium-templates',
    ],
    team: [
        // inherits pro
        'hosted-dashboard',
        'device-sync',
        'extended-storage',
        'premium-templates',
        // team-only
        'team-workspace',
        'roles-permissions',
        'approval-gates',
        'audit-log',
    ],
    enterprise: [
        // inherits team
        'hosted-dashboard',
        'device-sync',
        'extended-storage',
        'premium-templates',
        'team-workspace',
        'roles-permissions',
        'approval-gates',
        'audit-log',
        // enterprise-only
        'sso-saml',
        'immutable-audit-replay',
        'dedicated-hosted-workers',
        'compliance-pack',
        'private-skills-registry',
        'enterprise-agent-packs',
        'observability',
        'sla-support',
    ],
};
class LicenseManager {
    publicKey;
    constructor(publicKeyPem) {
        this.publicKey =
            publicKeyPem || process.env.OPENLIFE_LICENSE_PUBLIC_KEY || EMBEDDED_PUBLIC_KEY;
    }
    /** Validate a pasted key-id and extract its tier. Returns null if malformed. */
    parseKeyId(keyId) {
        const raw = (keyId || '').trim().toUpperCase();
        const m = KEY_ID_RE.exec(raw);
        if (!m)
            return null;
        return { tier: PREFIX_TIER[m[1]], raw };
    }
    /**
     * Deterministic JSON serialization for signing/verifying. Keys are sorted so
     * the signer (private repo) and verifier (here) hash identical bytes.
     */
    static canonicalize(payload) {
        const ordered = {};
        const src = payload;
        for (const key of Object.keys(payload).sort()) {
            ordered[key] = src[key];
        }
        return JSON.stringify(ordered);
    }
    /** Verify the Ed25519 signature over the token payload. */
    verifyToken(token) {
        if (!token || !token.payload || !token.signature) {
            return { valid: false, reason: 'malformed-token' };
        }
        let ok = false;
        try {
            ok = crypto.verify(null, Buffer.from(LicenseManager.canonicalize(token.payload)), this.publicKey, Buffer.from(token.signature, 'base64'));
        }
        catch (e) {
            return { valid: false, reason: `verify-error: ${e.message}` };
        }
        if (!ok)
            return { valid: false, reason: 'bad-signature' };
        // Signature is valid; now check the key-id format and tier coherence.
        const parsed = this.parseKeyId(token.payload.keyId);
        if (!parsed)
            return { valid: false, reason: 'bad-key-id-format' };
        if (parsed.tier !== token.payload.tier) {
            return { valid: false, reason: 'tier-prefix-mismatch' };
        }
        return { valid: true };
    }
    /** True when the token has not yet expired (the "em dia" check). */
    isCurrent(token, now = new Date()) {
        const exp = Date.parse(token.payload.expiresAt);
        if (Number.isNaN(exp))
            return false;
        return exp > now.getTime();
    }
    /** Resolve the feature set for a token: explicit grants win, else tier map. */
    entitlements(token) {
        if (token.payload.features && token.payload.features.length > 0) {
            return token.payload.features;
        }
        return exports.TIER_FEATURES[token.payload.tier] || [];
    }
}
exports.LicenseManager = LicenseManager;
