/**
 * Feature Flags Configuration
 *
 * Allows users to enable/disable features via `.vaspera/config.yaml`.
 *
 * Example configuration:
 * ```yaml
 * version: 1
 *
 * agents:
 *   security:
 *     enabled: true
 *     weight: 1.0
 *   reliability:
 *     enabled: true
 *     weight: 0.8
 *   typesafety:
 *     enabled: false
 *   redteam:
 *     enabled: true
 *     weight: 0.5
 *
 * scanners:
 *   semgrep:
 *     enabled: true
 *     timeout: 120000
 *   gitleaks:
 *     enabled: true
 *   npm-audit:
 *     enabled: true
 *
 * features:
 *   multiModel: false
 *   costTracking: true
 *   autofix: true
 *   incrementalScan: true
 * ```
 *
 * @module config/flags
 */
import { readFile, access, writeFile } from "fs/promises";
import { join } from "path";
import { z } from "zod";
import { logger } from "../logger.js";
/**
 * Agent configuration
 */
export const AgentConfigSchema = z.object({
    /** Whether agent is enabled */
    enabled: z.boolean().default(true),
    /** Agent weight for consensus (0-1) */
    weight: z.number().min(0).max(1).default(1.0),
    /** Model to use for this agent */
    model: z.string().optional(),
    /** Temperature for this agent */
    temperature: z.number().min(0).max(2).optional(),
    /** Custom prompt additions */
    promptAdditions: z.string().optional(),
});
/**
 * Scanner configuration
 */
export const ScannerConfigSchema = z.object({
    /** Whether scanner is enabled */
    enabled: z.boolean().default(true),
    /** Scanner timeout in ms */
    timeout: z.number().optional(),
    /** Custom rules directory */
    rulesDir: z.string().optional(),
    /** Additional scanner-specific options */
    options: z.record(z.unknown()).optional(),
});
/**
 * Feature flags
 */
export const FeatureFlagsSchema = z.object({
    /** Enable multi-model consensus */
    multiModel: z.boolean().default(false),
    /** Enable cost tracking */
    costTracking: z.boolean().default(true),
    /** Enable auto-fix suggestions */
    autofix: z.boolean().default(true),
    /** Enable incremental scanning */
    incrementalScan: z.boolean().default(true),
    /** Enable scanner caching */
    scannerCache: z.boolean().default(true),
    /** Enable cross-verification */
    crossVerification: z.boolean().default(true),
    /** Enable Sigstore signing */
    sigstoreSigning: z.boolean().default(true),
    /** Enable SARIF export */
    sarifExport: z.boolean().default(true),
    /** Enable compliance mapping */
    complianceMapping: z.boolean().default(true),
    /** Enable OpenTelemetry */
    otel: z.boolean().default(false),
    /** Enable plugin system */
    plugins: z.boolean().default(true),
    /** Enable red team mode */
    redteam: z.boolean().default(false),
});
/**
 * Full configuration schema
 */
export const VasperaConfigSchema = z.object({
    /** Config version */
    version: z.literal(1).default(1),
    /** Agent configurations */
    agents: z.record(AgentConfigSchema).default({}),
    /** Scanner configurations */
    scanners: z.record(ScannerConfigSchema).default({}),
    /** Feature flags */
    features: FeatureFlagsSchema.default({}),
    /** Default model for agents */
    defaultModel: z.string().default("claude-sonnet-4-20250514"),
    /** Default temperature */
    defaultTemperature: z.number().min(0).max(2).default(0.3),
    /** Certification settings */
    certification: z.object({
        /** Minimum confidence threshold */
        minConfidence: z.number().min(0).max(100).default(70),
        /** Required agent agreement ratio */
        requiredAgreement: z.number().min(0).max(1).default(0.6),
        /** Maximum findings per agent */
        maxFindingsPerAgent: z.number().default(100),
        /** Auto-finalize when complete */
        autoFinalize: z.boolean().default(false),
    }).default({}),
    /** Cost tracking settings */
    cost: z.object({
        /** Maximum budget per certification (USD) */
        maxBudget: z.number().optional(),
        /** Warning threshold (percentage of budget) */
        warningThreshold: z.number().min(0).max(1).default(0.8),
        /** Abort on budget exceeded */
        abortOnBudget: z.boolean().default(false),
    }).default({}),
    /** Plugin settings */
    plugins: z.object({
        /** Load plugins from .vaspera/plugins */
        loadLocal: z.boolean().default(true),
        /** Load plugins from npm */
        loadNpm: z.boolean().default(true),
        /** Run plugins in sandbox */
        sandbox: z.boolean().default(true),
        /** Plugin-specific configs */
        configs: z.record(z.record(z.unknown())).default({}),
    }).default({}),
});
/**
 * Default configuration
 */
export const DEFAULT_CONFIG = {
    version: 1,
    agents: {
        security: { enabled: true, weight: 1.0 },
        reliability: { enabled: true, weight: 0.8 },
        typesafety: { enabled: true, weight: 0.7 },
    },
    scanners: {
        semgrep: { enabled: true },
        "npm-audit": { enabled: true },
        gitleaks: { enabled: true },
        tsc: { enabled: true },
    },
    features: {
        multiModel: false,
        costTracking: true,
        autofix: true,
        incrementalScan: true,
        scannerCache: true,
        crossVerification: true,
        sigstoreSigning: true,
        sarifExport: true,
        complianceMapping: true,
        otel: false,
        plugins: true,
        redteam: false,
    },
    defaultModel: "claude-sonnet-4-20250514",
    defaultTemperature: 0.3,
    certification: {
        minConfidence: 70,
        requiredAgreement: 0.6,
        maxFindingsPerAgent: 100,
        autoFinalize: false,
    },
    cost: {
        warningThreshold: 0.8,
        abortOnBudget: false,
    },
    plugins: {
        loadLocal: true,
        loadNpm: true,
        sandbox: true,
        configs: {},
    },
};
/**
 * Cached configuration
 */
let cachedConfig = null;
let cachedProjectPath = null;
/**
 * Load configuration from .vaspera/config.yaml
 */
export async function loadConfig(projectPath) {
    // Return cached config if same project
    if (cachedConfig && cachedProjectPath === projectPath) {
        return cachedConfig;
    }
    const configPaths = [
        join(projectPath, ".vaspera", "config.yaml"),
        join(projectPath, ".vaspera", "config.yml"),
        join(projectPath, ".vaspera", "config.json"),
        join(projectPath, ".vasperarc"),
        join(projectPath, ".vasperarc.json"),
    ];
    for (const configPath of configPaths) {
        try {
            await access(configPath);
            const content = await readFile(configPath, "utf-8");
            // Parse based on extension
            let data;
            if (configPath.endsWith(".json")) {
                data = JSON.parse(content);
            }
            else {
                // Simple YAML parsing
                data = parseSimpleYaml(content);
            }
            const result = VasperaConfigSchema.safeParse(data);
            if (result.success) {
                logger.info("config.loaded", {
                    path: configPath,
                    agents: Object.keys(result.data.agents).length,
                    scanners: Object.keys(result.data.scanners).length,
                });
                // Cache the config
                cachedConfig = result.data;
                cachedProjectPath = projectPath;
                return result.data;
            }
            else {
                logger.warn("config.invalid", {
                    path: configPath,
                    errors: result.error.errors.map((e) => `${e.path.join(".")}: ${e.message}`),
                });
            }
        }
        catch {
            // File doesn't exist or can't be read
        }
    }
    // Return default config
    logger.debug("config.using_defaults", { projectPath });
    cachedConfig = DEFAULT_CONFIG;
    cachedProjectPath = projectPath;
    return DEFAULT_CONFIG;
}
/**
 * Clear config cache
 */
export function clearConfigCache() {
    cachedConfig = null;
    cachedProjectPath = null;
}
/**
 * Simple YAML parser for configuration files
 */
function parseSimpleYaml(content) {
    const lines = content.split("\n");
    const result = {};
    const stack = [
        { obj: result, indent: -2 },
    ];
    for (const line of lines) {
        // Skip comments and empty lines
        const trimmed = line.trim();
        if (trimmed.startsWith("#") || trimmed === "")
            continue;
        // Get indentation
        const match = line.match(/^(\s*)(.+)$/);
        if (!match)
            continue;
        const indent = match[1].length;
        const content = match[2];
        // Pop stack until we find parent
        while (stack.length > 1 && stack[stack.length - 1].indent >= indent) {
            stack.pop();
        }
        const parent = stack[stack.length - 1].obj;
        // Handle key: value
        if (content.includes(": ")) {
            const colonIndex = content.indexOf(": ");
            const key = content.slice(0, colonIndex).trim();
            const value = content.slice(colonIndex + 2).trim();
            if (value === "" || value === "|" || value === ">") {
                // Object or multiline string
                const newObj = {};
                parent[key] = newObj;
                stack.push({ obj: newObj, indent });
            }
            else {
                parent[key] = parseYamlValue(value);
            }
        }
        else if (content.endsWith(":")) {
            // New object
            const key = content.slice(0, -1).trim();
            const newObj = {};
            parent[key] = newObj;
            stack.push({ obj: newObj, indent });
        }
    }
    return result;
}
/**
 * Parse a YAML value
 */
function parseYamlValue(value) {
    // Remove quotes
    if ((value.startsWith('"') && value.endsWith('"')) ||
        (value.startsWith("'") && value.endsWith("'"))) {
        return value.slice(1, -1);
    }
    // Boolean
    if (value === "true")
        return true;
    if (value === "false")
        return false;
    // Null
    if (value === "null" || value === "~")
        return null;
    // Number
    const num = Number(value);
    if (!isNaN(num) && value !== "")
        return num;
    // String
    return value;
}
/**
 * Check if a feature is enabled
 */
export function isFeatureEnabled(config, feature) {
    return config.features[feature] ?? DEFAULT_CONFIG.features[feature] ?? false;
}
/**
 * Check if an agent is enabled
 */
export function isAgentEnabled(config, agent) {
    return config.agents[agent]?.enabled ?? true;
}
/**
 * Get agent weight
 */
export function getAgentWeight(config, agent) {
    return config.agents[agent]?.weight ?? 1.0;
}
/**
 * Check if a scanner is enabled
 */
export function isScannerEnabled(config, scanner) {
    return config.scanners[scanner]?.enabled ?? true;
}
/**
 * Get scanner timeout
 */
export function getScannerTimeout(config, scanner, defaultTimeout = 120000) {
    return config.scanners[scanner]?.timeout ?? defaultTimeout;
}
/**
 * Generate sample configuration file
 */
export function generateSampleConfig() {
    return `# Vaspera Hardening MCP Configuration
# Place this file at .vaspera/config.yaml

version: 1

# Agent configurations
agents:
  security:
    enabled: true
    weight: 1.0
  reliability:
    enabled: true
    weight: 0.8
  typesafety:
    enabled: true
    weight: 0.7
  redteam:
    enabled: false
    weight: 0.5

# Scanner configurations
scanners:
  semgrep:
    enabled: true
    timeout: 120000
  npm-audit:
    enabled: true
  gitleaks:
    enabled: true
  tsc:
    enabled: true
  eslint:
    enabled: false
  bandit:
    enabled: true  # Auto-enabled if Python detected
  gosec:
    enabled: true  # Auto-enabled if Go detected

# Feature flags
features:
  multiModel: false
  costTracking: true
  autofix: true
  incrementalScan: true
  scannerCache: true
  crossVerification: true
  sigstoreSigning: true
  sarifExport: true
  complianceMapping: true
  otel: false
  plugins: true
  redteam: false

# Default model settings
defaultModel: claude-sonnet-4-20250514
defaultTemperature: 0.3

# Certification settings
certification:
  minConfidence: 70
  requiredAgreement: 0.6
  maxFindingsPerAgent: 100
  autoFinalize: false

# Cost tracking settings
cost:
  maxBudget: 10.00  # USD
  warningThreshold: 0.8
  abortOnBudget: false

# Plugin settings
plugins:
  loadLocal: true
  loadNpm: true
  sandbox: true
  configs: {}
`;
}
/**
 * Save configuration to file
 */
export async function saveConfig(projectPath, config) {
    const configPath = join(projectPath, ".vaspera", "config.yaml");
    const content = generateSampleConfig(); // Use sample as template
    await writeFile(configPath, content, "utf-8");
    clearConfigCache();
    logger.info("config.saved", { path: configPath });
}
//# sourceMappingURL=flags.js.map