"use strict";
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
    for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CLIENT_HEADER_NAMES = void 0;
exports.loadClientMeta = loadClientMeta;
exports.getClientVersion = getClientVersion;
exports.getClientBuild = getClientBuild;
exports.getClientVersionLabel = getClientVersionLabel;
exports.createClientHeaderRecord = createClientHeaderRecord;
exports.validateClientEnvironment = validateClientEnvironment;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const constants_1 = require("./constants");
Object.defineProperty(exports, "CLIENT_HEADER_NAMES", { enumerable: true, get: function () { return constants_1.CLIENT_HEADER_NAMES; } });
function resolveMetaPath() {
    const candidates = Array.from(new Set([
        path_1.default.resolve(process.cwd(), 'config', 'client-meta.json'),
        path_1.default.resolve(process.cwd(), '..', 'config', 'client-meta.json'),
        path_1.default.resolve(process.cwd(), '..', '..', 'config', 'client-meta.json'),
        path_1.default.resolve(process.cwd(), '..', '..', '..', 'config', 'client-meta.json'),
        path_1.default.resolve(__dirname, '../../..', 'config', 'client-meta.json'),
        path_1.default.resolve(__dirname, '../../../..', 'config', 'client-meta.json')
    ]));
    for (const candidate of candidates) {
        if (fs_1.default.existsSync(candidate)) {
            return candidate;
        }
    }
    throw new Error(`missing_client_meta_json:${candidates.join('|')}`);
}
const META_PATH = resolveMetaPath();
let cachedMeta = null;
function loadClientMeta() {
    if (!cachedMeta) {
        const raw = fs_1.default.readFileSync(META_PATH, 'utf8');
        cachedMeta = JSON.parse(raw);
    }
    return cachedMeta;
}
function getClientVersion() {
    return loadClientMeta().version;
}
function getClientBuild() {
    return loadClientMeta().build;
}
function getClientVersionLabel() {
    const meta = loadClientMeta();
    return `${meta.version} (build ${meta.build})`;
}
__exportStar(require("./constants"), exports);
__exportStar(require("./deployment"), exports);
__exportStar(require("./types"), exports);
__exportStar(require("./server/fastify"), exports);
__exportStar(require("./server/logging"), exports);
function createClientHeaderRecord(info) {
    const headers = {};
    if (info.client)
        headers[constants_1.CLIENT_HEADER_NAMES.client] = info.client;
    if (info.clientVersion)
        headers[constants_1.CLIENT_HEADER_NAMES.clientVersion] = String(info.clientVersion);
    if (info.clientBuild !== undefined && info.clientBuild !== null && `${info.clientBuild}`.trim() !== '') {
        headers[constants_1.CLIENT_HEADER_NAMES.clientBuild] = String(info.clientBuild);
    }
    if (info.platform)
        headers[constants_1.CLIENT_HEADER_NAMES.platform] = info.platform;
    if (info.platformVersion)
        headers[constants_1.CLIENT_HEADER_NAMES.platformVersion] = String(info.platformVersion);
    return headers;
}
function collectMissingHeaders(info) {
    const headers = [
        { header: constants_1.CLIENT_HEADER_NAMES.client, value: info?.client },
        { header: constants_1.CLIENT_HEADER_NAMES.clientVersion, value: info?.clientVersion },
        { header: constants_1.CLIENT_HEADER_NAMES.clientBuild, value: info?.clientBuild },
        { header: constants_1.CLIENT_HEADER_NAMES.platform, value: info?.platform },
        { header: constants_1.CLIENT_HEADER_NAMES.platformVersion, value: info?.platformVersion },
    ];
    return headers
        .filter((entry) => {
        if (entry.value === undefined || entry.value === null) {
            return true;
        }
        return `${entry.value}`.trim().length === 0;
    })
        .map((entry) => entry.header);
}
function resolveClientRule(meta, clientKey) {
    return meta.clients[clientKey] ?? meta.clients['all'];
}
function validateClientEnvironment(info) {
    const normalized = normalizeRuntimeInfo(info);
    if (!normalized) {
        return {
            ok: false,
            code: 'missing_client_metadata',
            provided: null,
            missingHeaders: collectMissingHeaders(info),
        };
    }
    const meta = loadClientMeta();
    const clientKey = normalized.client;
    const clientRule = resolveClientRule(meta, clientKey);
    if (!clientRule) {
        return {
            ok: false,
            code: 'unsupported_client',
            provided: normalized
        };
    }
    const minimumVersion = clientRule.minimumVersion && clientRule.minimumVersion !== 'any'
        ? clientRule.minimumVersion
        : undefined;
    if (minimumVersion) {
        const compare = compareVersions(normalized.clientVersion, minimumVersion);
        if (compare < 0) {
            return {
                ok: false,
                code: 'unsupported_client_version',
                ...(minimumVersion ? { requiredVersion: minimumVersion } : {}),
                ...(typeof clientRule.minimumBuild === 'number' ? { requiredBuild: clientRule.minimumBuild } : {}),
                provided: normalized,
            };
        }
    }
    const shouldCheckBuild = typeof clientRule.minimumBuild === 'number' && (!minimumVersion || compareVersions(normalized.clientVersion, minimumVersion) === 0);
    if (shouldCheckBuild && typeof clientRule.minimumBuild === 'number') {
        if (normalized.clientBuild < clientRule.minimumBuild) {
            return {
                ok: false,
                code: 'unsupported_client_build',
                ...(minimumVersion ? { requiredVersion: minimumVersion } : {}),
                requiredBuild: clientRule.minimumBuild,
                provided: normalized,
            };
        }
    }
    const platformKey = normalized.platform;
    const platformRule = meta.platforms?.[platformKey] ?? meta.platforms?.['unknown'];
    if (platformRule && platformRule.minimumVersion && platformRule.minimumVersion !== 'any') {
        if (compareVersions(normalized.platformVersion, platformRule.minimumVersion) < 0) {
            return {
                ok: false,
                code: 'unsupported_platform_version',
                requiredPlatformVersion: platformRule.minimumVersion,
                provided: normalized
            };
        }
    }
    return { ok: true, normalized };
}
function normalizeRuntimeInfo(info) {
    if (!info)
        return null;
    const client = (info.client || '').trim().toLowerCase();
    const clientVersion = (info.clientVersion || '').trim();
    const platform = (info.platform || '').trim().toLowerCase();
    const platformVersion = (info.platformVersion || '').trim();
    if (!client || !clientVersion || !platform || !platformVersion) {
        return null;
    }
    const buildValue = info.clientBuild;
    if (buildValue === undefined || buildValue === null || `${buildValue}`.trim() === '') {
        return null;
    }
    const parsedBuild = Number(buildValue);
    if (!Number.isFinite(parsedBuild) || parsedBuild < 0) {
        return null;
    }
    return {
        client,
        clientVersion,
        clientBuild: parsedBuild,
        platform,
        platformVersion
    };
}
function compareVersions(a, b) {
    const pa = a.split('.').map((part) => parseInt(part, 10) || 0);
    const pb = b.split('.').map((part) => parseInt(part, 10) || 0);
    const length = Math.max(pa.length, pb.length);
    for (let i = 0; i < length; i++) {
        const na = pa[i] ?? 0;
        const nb = pb[i] ?? 0;
        if (na > nb)
            return 1;
        if (na < nb)
            return -1;
    }
    return 0;
}
