/**
 * Constitution Loader and Evaluator
 *
 * Loads constitution files and evaluates pattern approval
 * based on risk tolerance and conditions.
 *
 * @module autofix/constitution
 */
import { readFile } from "fs/promises";
import { join } from "path";
import { parse as parseYaml } from "yaml";
import { ConstitutionSchema, DEFAULT_CONSTITUTION, } from "./constitution.schema.js";
import { logger } from "../logger.js";
/**
 * Default constitution file locations (checked in order)
 */
export const CONSTITUTION_PATHS = [
    ".vaspera/constitution.yaml",
    ".vaspera/constitution.yml",
    ".vaspera/constitution.json",
    "vaspera.constitution.yaml",
    "vaspera.constitution.json",
];
/**
 * Load constitution from project directory
 */
export async function loadConstitution(projectPath, constitutionPath) {
    const pathsToTry = constitutionPath
        ? [constitutionPath]
        : CONSTITUTION_PATHS.map((p) => join(projectPath, p));
    for (const path of pathsToTry) {
        try {
            const content = await readFile(path, "utf-8");
            const parsed = path.endsWith(".json")
                ? JSON.parse(content)
                : parseYaml(content);
            const validated = ConstitutionSchema.parse(parsed);
            logger.info("constitution.loaded", { path });
            return validated;
        }
        catch {
            // Continue to next path
        }
    }
    // Return default constitution if none found
    logger.info("constitution.using_defaults", { projectPath });
    return DEFAULT_CONSTITUTION;
}
/**
 * Check if a pattern is approved for auto-application
 */
export function isPatternApproved(constitution, pattern, context) {
    // Find pattern-specific rule
    const patternRule = constitution.patterns.find((p) => p.patternId === pattern.patternId);
    // If no specific rule, use risk tolerance
    if (!patternRule) {
        return evaluateByRiskTolerance(constitution.riskTolerance, pattern);
    }
    // Check explicit approval
    if (!patternRule.autoApprove) {
        return {
            approved: false,
            reason: `Pattern ${pattern.patternId} requires manual review`,
            requiredReviewer: patternRule.requiredReviewer,
        };
    }
    // Check conditions
    if (patternRule.conditions) {
        const { allowedPaths, excludedPaths, maxLinesChanged, minSeverity } = patternRule.conditions;
        // Check allowed paths
        if (allowedPaths && allowedPaths.length > 0) {
            const inAllowed = allowedPaths.some((p) => context.filePath.includes(p));
            if (!inAllowed) {
                return {
                    approved: false,
                    reason: `File path ${context.filePath} not in allowed paths`,
                };
            }
        }
        // Check excluded paths
        if (excludedPaths && excludedPaths.length > 0) {
            const inExcluded = excludedPaths.some((p) => context.filePath.includes(p));
            if (inExcluded) {
                return {
                    approved: false,
                    reason: `File path ${context.filePath} is in excluded paths`,
                };
            }
        }
        // Check max lines changed
        if (maxLinesChanged !== undefined &&
            context.linesChanged !== undefined &&
            context.linesChanged > maxLinesChanged) {
            return {
                approved: false,
                reason: `Change affects ${context.linesChanged} lines, exceeds max ${maxLinesChanged}`,
            };
        }
        // Check min severity
        if (minSeverity) {
            const severityOrder = ["info", "low", "medium", "high", "critical"];
            const minIdx = severityOrder.indexOf(minSeverity);
            const actualIdx = severityOrder.indexOf(context.severity);
            if (actualIdx < minIdx) {
                return {
                    approved: false,
                    reason: `Severity ${context.severity} below minimum ${minSeverity}`,
                };
            }
        }
    }
    return { approved: true };
}
/**
 * Evaluate pattern approval by risk tolerance
 */
function evaluateByRiskTolerance(tolerance, pattern) {
    switch (tolerance) {
        case "conservative":
            // Only auto-apply patterns marked as safe with low risk
            if (pattern.safeToAutoApply && pattern.risk === "low") {
                return { approved: true };
            }
            return {
                approved: false,
                reason: "Conservative mode: only low-risk safe patterns auto-applied",
            };
        case "moderate":
            // Auto-apply safe patterns and low/medium risk
            if (pattern.safeToAutoApply || pattern.risk !== "high") {
                return { approved: true };
            }
            return {
                approved: false,
                reason: "Moderate mode: high-risk patterns require review",
            };
        case "aggressive":
            // Auto-apply all patterns
            return { approved: true };
    }
}
/**
 * Check if file path is allowed for autofix
 */
export function isPathAllowed(constitution, filePath) {
    const { neverAutofix, requireReview } = constitution.directories;
    // Check never-autofix directories
    const inNeverAutofix = neverAutofix.some((dir) => filePath.includes(dir));
    if (inNeverAutofix) {
        return {
            allowed: false,
            reason: `Path ${filePath} is in never-autofix directory`,
            requiresReview: false,
        };
    }
    // Check require-review directories (allowed but flagged)
    const inRequireReview = requireReview.some((dir) => filePath.includes(dir));
    if (inRequireReview) {
        return {
            allowed: true,
            reason: `Path ${filePath} requires additional review`,
            requiresReview: true,
        };
    }
    return { allowed: true, requiresReview: false };
}
/**
 * Get effective safeToAutoApply status for patterns based on constitution
 */
export function getEffectivePatterns(constitution, patterns) {
    return patterns.map((pattern) => {
        const rule = constitution.patterns.find((p) => p.patternId === pattern.patternId);
        if (rule) {
            return {
                ...pattern,
                safeToAutoApply: rule.autoApprove,
            };
        }
        // Apply risk tolerance
        const { approved } = evaluateByRiskTolerance(constitution.riskTolerance, pattern);
        return {
            ...pattern,
            safeToAutoApply: approved,
        };
    });
}
/**
 * Validate a constitution file
 */
export function validateConstitution(content) {
    const result = ConstitutionSchema.safeParse(content);
    if (result.success) {
        return { valid: true };
    }
    const errors = result.error.errors.map((e) => `${e.path.join(".")}: ${e.message}`);
    return { valid: false, errors };
}
/**
 * Generate a default constitution file content
 */
export function generateDefaultConstitution() {
    return `# Vaspera Autofix Constitution
# This file governs which patterns can be automatically applied
# and under what conditions.

version: "1.0"

# Risk tolerance: conservative | moderate | aggressive
# - conservative: Only auto-apply safe patterns with low risk
# - moderate: Auto-apply safe patterns and low/medium risk
# - aggressive: Auto-apply all patterns
riskTolerance: "conservative"

# Pattern-specific approvals
patterns:
  # Example: Auto-approve console.log removal
  - patternId: "qual-console-log"
    autoApprove: true

  # Example: Require security review for SQL fixes
  - patternId: "sec-sql-injection"
    autoApprove: false
    requiredReviewer: "security-team"

# PR creation rules
prRules:
  requireApproval: true
  minApprovers: 1
  requiredLabels:
    - autofix
    - security
  commitPrefix: "fix(security):"
  maxPRsPerRun: 10
  groupBy: "severity"

# Directory restrictions
directories:
  neverAutofix:
    - node_modules
    - vendor
    - .git
    - dist
  requireReview:
    - src/auth/
    - src/crypto/
    - src/payment/

# Safety constraints
safety:
  requireCleanWorkingTree: true
  createBackupBranch: true
  runTestsAfterFix: true
  testCommand: "npm test"
  revertOnTestFailure: true
  maxFilesPerRun: 20
  dryRunDefault: true
`;
}
// Re-export types and schema
export { ConstitutionSchema, DEFAULT_CONSTITUTION } from "./constitution.schema.js";
//# sourceMappingURL=constitution.js.map