/**
 * Pi Judgment Integration for Bolloon
 *
 * Core module for capturing, distilling, and applying human judgment.
 *
 * Architecture:
 * Human Input → Distillation Trigger → LLM Extraction → Judgment YAML
 *                              ↓
 *                      ValueFunction
 *                              ↓
 *                    Runtime Decision
 *
 * Storage: Hybrid mode
 * - High-frequency rules: Embedded in context-fragments/*.md YAML frontmatter
 * - Long-term preferences: .bolloon/judgments/*.yaml
 */
import * as fs from 'fs/promises';
import * as path from 'path';
import yaml from 'js-yaml';
const JUDGMENTS_DIR = path.join(process.env.HOME || '/tmp', '.bolloon', 'judgments');
const JUDGMENT_FILES = {
    rule: path.join(JUDGMENTS_DIR, 'rules.yaml'),
    preference: path.join(JUDGMENTS_DIR, 'preferences.yaml'),
    trajectory: path.join(JUDGMENTS_DIR, 'trajectories.yaml'),
    reward: path.join(JUDGMENTS_DIR, 'rewards.yaml'),
};
let judgmentCache = new Map();
let valueFunctionCache = null;
let cacheDirty = true;
let storeInitialized = false;
function generateJudgmentId() {
    return `jdg-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
}
/**
 * Initialize the judgment store, creating all YAML files if they don't exist
 */
export async function initializeJudgmentStore() {
    if (storeInitialized)
        return;
    await fs.mkdir(JUDGMENTS_DIR, { recursive: true });
    const emptyYaml = '# Auto-generated by Pi Judgment System\n# Do not edit manually\n\njudgments: []\n';
    for (const [type, filePath] of Object.entries(JUDGMENT_FILES)) {
        try {
            await fs.access(filePath);
        }
        catch {
            await fs.writeFile(filePath, emptyYaml, 'utf-8');
            console.log(`[Judgment] Created ${type} store at ${filePath}`);
        }
    }
    storeInitialized = true;
    cacheDirty = true;
}
/**
 * Bootstrap the entire judgment system - initializes all stores and LLM
 */
export async function bootstrapJudgmentSystem() {
    console.log('[Judgment] Bootstrapping judgment system...');
    await initializeJudgmentStore();
    const { initializeValueStore } = await import('./human-value-store.js');
    await initializeValueStore();
    const { initializeDistillation } = await import('./distillation.js');
    await initializeDistillation();
    cacheDirty = true;
    valueFunctionCache = null;
    console.log('[Judgment] Bootstrap complete');
}
/**
 * Load all judgments from YAML files
 */
export async function loadJudgments() {
    if (!storeInitialized) {
        await initializeJudgmentStore();
    }
    const judgments = new Map();
    for (const [type, filePath] of Object.entries(JUDGMENT_FILES)) {
        try {
            const content = await fs.readFile(filePath, 'utf-8');
            const data = parseYaml(content);
            if (data && Array.isArray(data)) {
                judgments.set(type, data);
            }
            else if (data && 'judgments' in data && Array.isArray(data.judgments)) {
                judgments.set(type, data.judgments);
            }
        }
        catch {
            judgments.set(type, []);
        }
    }
    judgmentCache = judgments;
    cacheDirty = false;
    return judgments;
}
/**
 * Save judgments to YAML file
 */
async function saveJudgments(type, judgments) {
    await fs.mkdir(JUDGMENTS_DIR, { recursive: true });
    const filePath = JUDGMENT_FILES[type];
    if (!filePath)
        return;
    const body = yaml.dump({ judgments });
    const header = '# Auto-generated by Pi Judgment System\n# Do not edit manually\n\n';
    await fs.writeFile(filePath, header + body, 'utf-8');
    cacheDirty = true;
}
/**
 * Create a new judgment
 */
export async function createJudgment(params) {
    const judgment = {
        id: generateJudgmentId(),
        type: params.type,
        content: params.content,
        source: params.source,
        confidence: params.confidence,
        context: params.context,
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString(),
        evidence: params.evidence,
    };
    const judgments = await loadJudgments();
    const typeJudgments = judgments.get(params.type) || [];
    typeJudgments.push(judgment);
    judgments.set(params.type, typeJudgments);
    await saveJudgments(params.type, typeJudgments);
    console.log(`[Judgment] Created ${judgment.id} (${params.type}): ${params.content.substring(0, 50)}...`);
    return judgment;
}
/**
 * Update judgment confidence based on feedback
 */
export async function updateJudgmentConfidence(id, delta, feedbackType) {
    const judgments = await loadJudgments();
    for (const [type, typeJudgments] of judgments.entries()) {
        const judgment = typeJudgments.find((j) => j.id === id);
        if (judgment) {
            let confidenceChange = delta;
            if (feedbackType === 'approve') {
                confidenceChange = Math.min(delta, 1 - judgment.confidence);
            }
            else if (feedbackType === 'reject') {
                confidenceChange = -delta;
            }
            else if (feedbackType === 'correct') {
                confidenceChange = delta * 2;
            }
            judgment.confidence = Math.max(0, Math.min(1, judgment.confidence + confidenceChange));
            judgment.updatedAt = new Date().toISOString();
            await saveJudgments(type, typeJudgments);
            return judgment;
        }
    }
    return null;
}
/**
 * Get all judgments
 */
export async function getAllJudgments() {
    const judgments = await loadJudgments();
    const all = [];
    for (const typeJudgments of judgments.values()) {
        all.push(...typeJudgments);
    }
    return all;
}
/**
 * Get judgments by type
 */
export async function getJudgmentsByType(type) {
    const judgments = await loadJudgments();
    return judgments.get(type) || [];
}
/**
 * Get judgments relevant to a context
 */
export async function getJudgmentsForContext(context) {
    const all = await getAllJudgments();
    return all.filter((j) => {
        if (!j.context)
            return false;
        return j.context.toLowerCase().includes(context.toLowerCase());
    });
}
/**
 * Calculate confidence score for a decision
 */
export function calculateConfidence(judgments) {
    if (judgments.length === 0)
        return 0.5;
    let totalSourceWeight = 0;
    let weightedConfidenceSum = 0;
    for (const j of judgments) {
        const sourceWeight = j.source === 'human' ? 1.5 : 1.0;
        weightedConfidenceSum += j.confidence * sourceWeight;
        totalSourceWeight += sourceWeight;
    }
    return totalSourceWeight > 0 ? weightedConfidenceSum / totalSourceWeight : 0.5;
}
/**
 * Build ValueFunction from judgments
 */
export async function buildValueFunction(context) {
    const judgments = context
        ? await getJudgmentsForContext(context)
        : await getAllJudgments();
    const contextWeights = {};
    const contextCounts = {};
    for (const j of judgments) {
        const ctx = j.context || 'general';
        contextCounts[ctx] = (contextCounts[ctx] || 0) + 1;
    }
    for (const [ctx, count] of Object.entries(contextCounts)) {
        contextWeights[ctx] = count / judgments.length;
    }
    return {
        judgments,
        contextWeights,
        lastUpdated: new Date().toISOString(),
    };
}
/**
 * Get cached ValueFunction
 */
export async function getValueFunction(context) {
    if (!valueFunctionCache || cacheDirty) {
        valueFunctionCache = await buildValueFunction(context);
    }
    return valueFunctionCache;
}
/**
 * Extract judgments from context fragment YAML frontmatter
 * Falls back to using file content + filename as context when no frontmatter judgment exists
 */
export async function extractFromFragment(fragmentPath) {
    try {
        const content = await fs.readFile(fragmentPath, 'utf-8');
        const frontmatter = extractFrontmatter(content);
        const context = path.basename(fragmentPath, '.md');
        const markdownContent = extractMarkdownContent(content);
        if (frontmatter.judgment) {
            const j = frontmatter.judgment;
            return [{
                    id: generateJudgmentId(),
                    type: j.type || 'rule',
                    content: markdownContent,
                    source: j.source || 'human',
                    confidence: typeof j.confidence === 'number' ? j.confidence : 0.9,
                    context: j.category || context,
                    createdAt: j.last_reviewed || new Date().toISOString(),
                    updatedAt: new Date().toISOString(),
                    metadata: { source: 'fragment', fragment: fragmentPath },
                }];
        }
        if (!markdownContent || markdownContent.trim().length < 10) {
            return [];
        }
        const firstLine = markdownContent.split('\n').find(l => l.trim().length > 0) || '';
        const headingMatch = firstLine.match(/^#+\s*(.+)/);
        const ruleContent = headingMatch
            ? `${headingMatch[1].trim()} - ${markdownContent.substring(0, 200).replace(/^#+\s*.+\n/, '').trim().substring(0, 150)}`
            : markdownContent.substring(0, 200).split('\n')[0]?.trim() || firstLine;
        return [{
                id: generateJudgmentId(),
                type: 'rule',
                content: ruleContent,
                source: 'human',
                confidence: 0.8,
                context,
                createdAt: new Date().toISOString(),
                updatedAt: new Date().toISOString(),
                metadata: { source: 'fragment', fragment: fragmentPath, autoExtracted: true },
            }];
    }
    catch {
        return [];
    }
}
/**
 * Load judgments from all context fragments
 */
export async function loadFragmentJudgments() {
    const FRAGMENTS_DIR = path.join(process.cwd(), 'src', 'bollharness', 'scripts', 'context-fragments');
    const fragmentJudgments = [];
    try {
        const files = await fs.readdir(FRAGMENTS_DIR);
        for (const file of files) {
            if (file.endsWith('.md')) {
                const judgments = await extractFromFragment(path.join(FRAGMENTS_DIR, file));
                fragmentJudgments.push(...judgments);
            }
        }
    }
    catch {
        // Fragment directory doesn't exist
    }
    return fragmentJudgments;
}
/**
 * Get combined judgments (file + fragments)
 */
export async function getCombinedJudgments() {
    const [fileJudgments, fragmentJudgments] = await Promise.all([
        getAllJudgments(),
        loadFragmentJudgments(),
    ]);
    const seen = new Set();
    const combined = [];
    for (const j of fragmentJudgments) {
        combined.push(j);
        seen.add(j.context || j.id);
    }
    for (const j of fileJudgments) {
        if (!seen.has(j.context || j.id)) {
            combined.push(j);
        }
    }
    return combined;
}
/**
 * Simple YAML parser (handles basic judgment format)
 */
function parseYaml(content) {
    try {
        if (!content.trim())
            return [];
        return yaml.load(content) ?? [];
    }
    catch {
        return [];
    }
}
/**
 * (YAML serialization now delegated to js-yaml)
 */
/**
 * Best-effort coercion for a scalar frontmatter value.
 * Returns booleans, numbers, or the raw string.
 */
function parseFrontmatterValue(raw) {
    const value = raw.trim();
    if (value === '')
        return '';
    if (value === 'true')
        return true;
    if (value === 'false')
        return false;
    if (value === 'null' || value === '~')
        return null;
    if (/^-?\d+(\.\d+)?$/.test(value))
        return Number(value);
    // Strip surrounding quotes if present
    if ((value.startsWith('"') && value.endsWith('"')) ||
        (value.startsWith("'") && value.endsWith("'"))) {
        return value.slice(1, -1);
    }
    return value;
}
/**
 * Extract YAML frontmatter from markdown
 */
function extractFrontmatter(content) {
    const match = content.match(/^---\n([\s\S]*?)\n---/);
    if (!match)
        return {};
    // Try js-yaml first; fall back to simple line parser if it fails or types missing.
    try {
        const parsed = yaml.load(match[1]);
        if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
            return parsed;
        }
    }
    catch {
        // fall through to manual parsing
    }
    const frontmatter = {};
    const lines = match[1].split('\n');
    let currentKey = '';
    let currentIndent = 0;
    for (const line of lines) {
        const indent = line.search(/\S/);
        const trimmed = line.trim();
        if (trimmed.startsWith('judgment:')) {
            currentKey = 'judgment';
            frontmatter[currentKey] = {};
        }
        else if (trimmed.startsWith('-')) {
            // Array item
        }
        else if (trimmed.includes(':')) {
            const [key, ...valueParts] = trimmed.split(':');
            const value = valueParts.join(':').trim();
            if (currentKey === 'judgment' && indent > 0) {
                frontmatter[currentKey][key] = parseFrontmatterValue(value);
            }
            else {
                frontmatter[key.trim()] = parseFrontmatterValue(value);
                currentKey = key.trim();
            }
        }
    }
    return frontmatter;
}
/**
 * Extract markdown content (after frontmatter)
 */
function extractMarkdownContent(content) {
    const match = content.match(/^---\n[\s\S]*?\n---\n([\s\S]*)$/);
    return match ? match[1].trim() : content;
}
/**
 * Get judgment statistics
 */
export async function getJudgmentStats() {
    const judgments = await getAllJudgments();
    const byType = {
        rule: 0,
        preference: 0,
        trajectory: 0,
        reward: 0,
    };
    const bySource = {
        human: 0,
        agent: 0,
        collaboration: 0,
    };
    let totalConfidence = 0;
    for (const j of judgments) {
        byType[j.type]++;
        bySource[j.source]++;
        totalConfidence += j.confidence;
    }
    return {
        total: judgments.length,
        byType,
        bySource,
        averageConfidence: judgments.length > 0 ? totalConfidence / judgments.length : 0,
    };
}
/**
 * Clear judgment cache
 */
export function clearCache() {
    valueFunctionCache = null;
    cacheDirty = true;
    judgmentCache = new Map();
}
/**
 * Reset judgment store initialization (forces re-initialization on next use)
 */
export function resetJudgmentStore() {
    storeInitialized = false;
    clearCache();
}
