"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 __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.loadRemotePolicyWithStatus = loadRemotePolicyWithStatus;
exports.loadRemotePolicy = loadRemotePolicy;
const https = __importStar(require("node:https"));
const node_net_1 = require("node:net");
const operationalLogging_1 = require("../operationalLogging");
function isPrivateHostName(hostname) {
    const lower = hostname.toLowerCase();
    if (lower === 'localhost') {
        return true;
    }
    if (lower === '::1' || lower === '[::1]') {
        return true;
    }
    if (lower.endsWith('.local')) {
        return true;
    }
    const ipVersion = (0, node_net_1.isIP)(lower);
    if (ipVersion === 4) {
        const parts = lower.split('.').map((segment) => Number(segment));
        const [a, b] = parts;
        if (a === 10) {
            return true;
        }
        if (a === 127) {
            return true;
        }
        if (a === 192 && b === 168) {
            return true;
        }
        if (a === 172 && b >= 16 && b <= 31) {
            return true;
        }
        if (a === 169 && b === 254) {
            return true;
        }
    }
    if (ipVersion === 6) {
        if (lower === '::1') {
            return true;
        }
        if (lower.startsWith('fc') || lower.startsWith('fd')) {
            return true;
        }
        if (lower.startsWith('fe80:')) {
            return true;
        }
    }
    return false;
}
function validatePolicySourceUrl(value) {
    let parsed;
    try {
        parsed = new URL(value);
    }
    catch {
        return { valid: false, message: 'Remote policy source URL is invalid. Using local policy only.' };
    }
    if (parsed.protocol === 'file:') {
        return {
            valid: false,
            message: 'Remote policy source URL cannot use file://. Using local policy only.',
        };
    }
    if (parsed.protocol !== 'https:') {
        return {
            valid: false,
            message: 'Remote policy source URL must use HTTPS. Using local policy only.',
        };
    }
    if (isPrivateHostName(parsed.hostname)) {
        return {
            valid: false,
            message: 'Remote policy source URL host is not allowed for security reasons. Using local policy only.',
        };
    }
    return { valid: true, url: parsed };
}
function normalizeRemotePolicy(value) {
    if (!value || typeof value !== 'object' || Array.isArray(value)) {
        return undefined;
    }
    const typed = value;
    if (typed.rules !== undefined &&
        (typed.rules === null || typeof typed.rules !== 'object' || Array.isArray(typed.rules))) {
        return undefined;
    }
    if (typed.dependencyRules !== undefined &&
        (typed.dependencyRules === null ||
            typeof typed.dependencyRules !== 'object' ||
            Array.isArray(typed.dependencyRules))) {
        return undefined;
    }
    const rules = typed.rules && typeof typed.rules === 'object'
        ? Object.entries(typed.rules)
            .filter(([, severity]) => typeof severity === 'string')
            .sort(([left], [right]) => left.localeCompare(right))
            .reduce((accumulator, [ruleId, severity]) => {
            accumulator[ruleId] = severity;
            return accumulator;
        }, {})
        : undefined;
    const dependencyRules = typed.dependencyRules && typeof typed.dependencyRules === 'object'
        ? Object.entries(typed.dependencyRules)
            .sort(([left], [right]) => left.localeCompare(right))
            .reduce((accumulator, [key, valueEntry]) => {
            accumulator[key] = valueEntry;
            return accumulator;
        }, {})
        : undefined;
    return {
        ...(rules ? { rules } : {}),
        ...(dependencyRules ? { dependencyRules } : {}),
    };
}
async function loadRemotePolicyWithStatus(policySourceUrl, options) {
    const validated = validatePolicySourceUrl(policySourceUrl);
    if (!validated.valid) {
        (0, operationalLogging_1.emitOperationalMessage)(options?.onOperationalMessage, 'warn', validated.message);
        return {
            message: validated.message,
        };
    }
    (0, operationalLogging_1.emitOperationalMessage)(options?.onOperationalMessage, 'info', `Remote policy configured. Loading policy from ${policySourceUrl}.`);
    return new Promise((resolve) => {
        const request = https.request({
            protocol: validated.url.protocol,
            hostname: validated.url.hostname,
            port: validated.url.port,
            path: `${validated.url.pathname}${validated.url.search}`,
            method: 'GET',
            headers: {
                'User-Agent': 'archpilot-cli',
                Accept: 'application/json',
            },
        }, (response) => {
            const chunks = [];
            response.on('data', (chunk) => {
                chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
            });
            response.on('end', () => {
                const body = Buffer.concat(chunks).toString('utf8');
                const statusCode = response.statusCode ?? 0;
                if (statusCode < 200 || statusCode >= 300) {
                    (0, operationalLogging_1.emitOperationalMessage)(options?.onOperationalMessage, 'warn', `Remote policy request failed with HTTP ${statusCode}. Using local policy only.`);
                    resolve({
                        message: `Remote policy request failed with HTTP ${statusCode}. Using local policy only.`,
                    });
                    return;
                }
                let parsed;
                try {
                    parsed = JSON.parse(body);
                }
                catch {
                    (0, operationalLogging_1.emitOperationalMessage)(options?.onOperationalMessage, 'warn', 'Remote policy payload is invalid JSON. Using local policy only.');
                    resolve({
                        message: 'Remote policy payload is invalid JSON. Using local policy only.',
                    });
                    return;
                }
                const normalized = normalizeRemotePolicy(parsed);
                if (!normalized) {
                    (0, operationalLogging_1.emitOperationalMessage)(options?.onOperationalMessage, 'warn', 'Remote policy payload has an invalid structure. Using local policy only.');
                    resolve({
                        message: 'Remote policy payload has an invalid structure. Using local policy only.',
                    });
                    return;
                }
                (0, operationalLogging_1.emitOperationalMessage)(options?.onOperationalMessage, 'info', 'Remote policy loaded successfully.');
                resolve({
                    policy: normalized,
                    message: 'Remote policy loaded successfully.',
                });
            });
        });
        request.setTimeout(5000, () => {
            request.destroy(new Error('timeout'));
        });
        request.on('error', () => {
            (0, operationalLogging_1.emitOperationalMessage)(options?.onOperationalMessage, 'warn', 'Remote policy unavailable. Using local policy only.');
            resolve({
                message: 'Remote policy unavailable. Using local policy only.',
            });
        });
        request.end();
    });
}
async function loadRemotePolicy(policySourceUrl) {
    const result = await loadRemotePolicyWithStatus(policySourceUrl);
    return result.policy;
}
//# sourceMappingURL=remotePolicyLoader.js.map