/**
 * 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';
const JUDGMENTS_DIR = path.join(process.env.HOME || '/tmp', '.bolloon', 'judgments');
const JUDGMENT_FILES = {
    rules: path.join(JUDGMENTS_DIR, 'rules.yaml'),
    preferences: path.join(JUDGMENTS_DIR, 'preferences.yaml'),
    trajectories: path.join(JUDGMENTS_DIR, 'trajectories.yaml'),
    rewards: path.join(JUDGMENTS_DIR, 'rewards.yaml'),
};
let judgmentCache = new Map();
let valueFunctionCache = null;
let cacheDirty = true;
function generateJudgmentId() {
    return `jdg-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
}
/**
 * Load all judgments from YAML files
 */
export async function loadJudgments() {
    const judgments = new Map();
    await fs.mkdir(JUDGMENTS_DIR, { recursive: true });
    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 yaml = serializeYaml({ judgments });
    await fs.writeFile(filePath, yaml, '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 totalWeight = 0;
    let weightedSum = 0;
    for (const j of judgments) {
        const weight = j.confidence * (j.source === 'human' ? 1.5 : 1.0);
        weightedSum += weight;
        totalWeight += weight;
    }
    return totalWeight > 0 ? weightedSum / totalWeight : 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
 */
export async function extractFromFragment(fragmentPath) {
    try {
        const content = await fs.readFile(fragmentPath, 'utf-8');
        const frontmatter = extractFrontmatter(content);
        if (!frontmatter.judgment)
            return [];
        const j = frontmatter.judgment;
        return [{
                id: generateJudgmentId(),
                type: j.type || 'rule',
                content: extractMarkdownContent(content),
                source: j.source || 'human',
                confidence: typeof j.confidence === 'number' ? j.confidence : 0.9,
                context: j.category || path.basename(fragmentPath, '.md'),
                createdAt: j.last_reviewed || new Date().toISOString(),
                updatedAt: new Date().toISOString(),
                metadata: { source: 'fragment', fragment: fragmentPath },
            }];
    }
    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 [];
        const data = {};
        const lines = content.split('\n');
        let inArray = false;
        let arrayKey = '';
        const arrayItems = [];
        for (const line of lines) {
            if (line.trim().startsWith('judgments:')) {
                inArray = true;
                continue;
            }
            if (inArray && line.trim().startsWith('-')) {
                const item = {};
                let currentKey = '';
                let currentValue = '';
                const itemContent = line.trim().substring(1);
                const kvMatch = itemContent.match(/^(\w+):\s*(.*)/);
                if (kvMatch) {
                    currentKey = kvMatch[1];
                    currentValue = kvMatch[2];
                    item[currentKey] = parseValue(currentValue);
                }
                arrayItems.push(item);
                continue;
            }
            if (inArray) {
                const kvMatch = line.match(/^(\w+):\s*(.*)/);
                if (kvMatch) {
                    const key = kvMatch[1];
                    const value = parseValue(kvMatch[2]);
                    data[key] = value;
                }
            }
        }
        if (arrayItems.length > 0) {
            data['judgments'] = arrayItems;
        }
        return data;
    }
    catch {
        return [];
    }
}
function parseValue(value) {
    const trimmed = value.trim();
    if (trimmed === 'true')
        return true;
    if (trimmed === 'false')
        return false;
    if (trimmed === 'null' || trimmed === 'undefined')
        return null;
    if (!isNaN(Number(trimmed)) && trimmed !== '')
        return Number(trimmed);
    return trimmed;
}
/**
 * Simple YAML serializer
 */
function serializeYaml(data) {
    const lines = ['# Auto-generated by Pi Judgment System', '# Do not edit manually', ''];
    if (typeof data === 'object' && data !== null) {
        lines.push('judgments:');
        const d = data;
        const arr = d.judgments;
        if (Array.isArray(arr)) {
            for (const item of arr) {
                lines.push('  - ' + serializeObject(item, 4));
            }
        }
    }
    return lines.join('\n');
}
function serializeObject(obj, indent) {
    if (typeof obj !== 'object' || obj === null) {
        return String(obj);
    }
    const spaces = ' '.repeat(indent);
    const innerSpace = ' '.repeat(indent + 2);
    const parts = [];
    for (const [key, value] of Object.entries(obj)) {
        if (value === undefined || value === null)
            continue;
        if (typeof value === 'object' && !Array.isArray(value)) {
            parts.push(`${key}:`);
            parts.push(serializeObject(value, indent + 2));
        }
        else if (Array.isArray(value)) {
            parts.push(`${key}:`);
            for (const item of value) {
                parts.push(`${innerSpace}- ${serializeObject(item, indent + 4)}`);
            }
        }
        else {
            const strValue = typeof value === 'string' ? `"${value}"` : String(value);
            parts.push(`${key}: ${strValue}`);
        }
    }
    if (parts.length === 1 && !parts[0].includes(':')) {
        return parts[0];
    }
    return parts.join(`\n${spaces}`);
}
/**
 * Extract YAML frontmatter from markdown
 */
function extractFrontmatter(content) {
    const match = content.match(/^---\n([\s\S]*?)\n---/);
    if (!match)
        return {};
    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] = parseValue(value);
            }
            else {
                frontmatter[key.trim()] = parseValue(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;
}
