"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseLicenseFile = parseLicenseFile;
exports.parseLicenseFileContents = parseLicenseFileContents;
exports.encodeCanonicalLicensePayload = encodeCanonicalLicensePayload;
exports.verifyLicenseFileSignature = verifyLicenseFileSignature;
const node_fs_1 = __importDefault(require("node:fs"));
const node_crypto_1 = __importDefault(require("node:crypto"));
const license_public_keys_1 = require("./license-public-keys");
const MVP_REQUIRED_PAYLOAD_FIELDS = [
    "license_id",
    "product",
    "licensed_to",
    "license_type",
    "issued_at",
    "entitlement",
    "features"
];
function parseLicenseFile(filePath) {
    return parseLicenseFileContents(node_fs_1.default.readFileSync(filePath, "utf8"));
}
function parseLicenseFileContents(contents) {
    let decoded;
    try {
        decoded = JSON.parse(contents);
    }
    catch {
        return malformed("malformed_json");
    }
    return parseLicenseEnvelope(decoded);
}
function encodeCanonicalLicensePayload(payload) {
    const canonicalJson = encodeCanonicalJsonValue(payload);
    if (typeof canonicalJson !== "string") {
        return {
            ok: false,
            reason: canonicalJson.reason
        };
    }
    return {
        ok: true,
        canonicalJson,
        bytes: Buffer.from(canonicalJson, "utf8")
    };
}
function verifyLicenseFileSignature(parsed) {
    if (parsed.signature.algorithm !== "Ed25519") {
        return {
            ok: false,
            reason: "unsupported_algorithm"
        };
    }
    const publicKey = (0, license_public_keys_1.findBundledLicensePublicKey)(parsed.signature.keyId);
    if (publicKey === null) {
        return {
            ok: false,
            reason: "unsupported_key_id"
        };
    }
    const signature = decodeEd25519Signature(parsed.signature.value);
    if (signature === null) {
        return {
            ok: false,
            reason: "malformed_signature"
        };
    }
    const canonicalPayload = encodeCanonicalLicensePayload(parsed.rawPayload);
    if (!canonicalPayload.ok) {
        return {
            ok: false,
            reason: "canonical_payload_not_encodable"
        };
    }
    try {
        const key = node_crypto_1.default.createPublicKey({
            key: Buffer.from(publicKey.publicKeySpkiDerBase64, "base64"),
            format: "der",
            type: "spki"
        });
        return node_crypto_1.default.verify(null, canonicalPayload.bytes, key, signature)
            ? { ok: true }
            : { ok: false, reason: "signature_mismatch" };
    }
    catch {
        return {
            ok: false,
            reason: "signature_mismatch"
        };
    }
}
function parseLicenseEnvelope(value) {
    if (!isRecord(value)) {
        return malformed("malformed_envelope");
    }
    if (value.license_schema_version !== 1) {
        return value.license_schema_version === undefined
            ? malformed("malformed_envelope")
            : malformed("unsupported_schema_version");
    }
    if (!isRecord(value.payload)) {
        return malformed("malformed_payload");
    }
    if (!hasRequiredPayloadFields(value.payload)) {
        return malformed("malformed_payload");
    }
    if (!isRecord(value.payload.entitlement) || !Array.isArray(value.payload.features)) {
        return malformed("malformed_payload");
    }
    const signature = parseSignature(value.signature);
    if (signature === null) {
        return malformed("malformed_signature");
    }
    return {
        ok: true,
        malformed: false,
        parsed: {
            licenseSchemaVersion: 1,
            rawPayload: value.payload,
            safePayload: extractSafePayload(value.payload),
            signature
        }
    };
}
function parseSignature(value) {
    if (!isRecord(value)) {
        return null;
    }
    const algorithm = value.algorithm;
    const keyId = value.key_id;
    const signatureValue = value.value;
    if (typeof algorithm !== "string" ||
        typeof keyId !== "string" ||
        typeof signatureValue !== "string") {
        return null;
    }
    return {
        algorithm,
        keyId,
        value: signatureValue
    };
}
function extractSafePayload(payload) {
    const entitlement = isRecord(payload.entitlement) ? payload.entitlement : {};
    const features = Array.isArray(payload.features) ? payload.features : [];
    return {
        licenseId: stringValue(payload.license_id),
        product: stringValue(payload.product),
        licensedParty: stringValue(payload.licensed_to),
        licenseType: stringValue(payload.license_type),
        expiresAtPresent: Object.hasOwn(payload, "expires_at"),
        expiresAt: nullableStringValue(payload.expires_at),
        entitlementKind: stringValue(entitlement.kind),
        entitlementVersion: stringValue(entitlement.version),
        upgradeFromLicenseId: nullableStringValue(entitlement.upgrade_from_license_id),
        features: features.filter((feature) => typeof feature === "string"),
        featureValuesAreStrings: features.every((feature) => typeof feature === "string")
    };
}
function hasRequiredPayloadFields(payload) {
    return MVP_REQUIRED_PAYLOAD_FIELDS.every((field) => Object.hasOwn(payload, field));
}
function malformed(reason) {
    return {
        ok: false,
        malformed: true,
        reason
    };
}
function encodeCanonicalJsonValue(value) {
    if (value === null) {
        return "null";
    }
    switch (typeof value) {
        case "string":
            return JSON.stringify(value);
        case "boolean":
            return value ? "true" : "false";
        case "number":
            return Number.isFinite(value) ? JSON.stringify(value) : { reason: "non_finite_number" };
        case "object":
            return Array.isArray(value)
                ? encodeCanonicalJsonArray(value)
                : encodeCanonicalJsonObject(value);
        default:
            return { reason: "unsupported_json_value" };
    }
}
function encodeCanonicalJsonArray(values) {
    const encodedValues = [];
    for (const value of values) {
        const encodedValue = encodeCanonicalJsonValue(value);
        if (typeof encodedValue !== "string") {
            return encodedValue;
        }
        encodedValues.push(encodedValue);
    }
    return `[${encodedValues.join(",")}]`;
}
function encodeCanonicalJsonObject(value) {
    const encodedProperties = [];
    for (const key of Object.keys(value).sort()) {
        const encodedValue = encodeCanonicalJsonValue(value[key]);
        if (typeof encodedValue !== "string") {
            return encodedValue;
        }
        encodedProperties.push(`${JSON.stringify(key)}:${encodedValue}`);
    }
    return `{${encodedProperties.join(",")}}`;
}
function isRecord(value) {
    return typeof value === "object" && value !== null && !Array.isArray(value);
}
function stringValue(value) {
    return typeof value === "string" ? value : undefined;
}
function nullableStringValue(value) {
    if (value === null) {
        return null;
    }
    return stringValue(value);
}
function decodeEd25519Signature(value) {
    if (!/^[A-Za-z0-9_-]+$/.test(value) || value.length % 4 === 1) {
        return null;
    }
    try {
        const decoded = Buffer.from(value, "base64url");
        return decoded.length === 64 ? decoded : null;
    }
    catch {
        return null;
    }
}
