/**
 * ============================================================
 * Procedural Memory (절차 기억)
 * 
 * 반복 작업의 절차와 패턴을 관리합니다.
 * 기존 skill.md 시스템과 통합되어, Brain의 memory/procedures/ 아래의
 * MD 파일을 스캔하여 절차를 로드합니다.
 * 저장 위치: {brainPath}/memory/procedures/*.md
 * ============================================================
 */

import * as fs from 'fs';
import * as path from 'path';
import { ProceduralEntry, MemoryContextResult } from './types';

export class ProceduralMemory {
    private procedures: ProceduralEntry[] = [];
    private procedureDir: string;
    private loaded = false;

    constructor(brainPath: string) {
        this.procedureDir = path.join(brainPath, 'memory', 'procedures');
        if (!fs.existsSync(this.procedureDir)) {
            fs.mkdirSync(this.procedureDir, { recursive: true });
        }
    }

    // ─── Loading ───

    /**
     * procedures/ 디렉토리에서 MD 파일을 스캔하여 절차를 로드합니다.
     * 
     * MD 파일 형식:
     * ```
     * ---
     * name: P-Reinforce Wikification
     * triggers: ["wiki화", "위키", "wikify"]
     * ---
     * 1. 첫 번째 단계
     * 2. 두 번째 단계
     * ```
     */
    public loadProcedures(): ProceduralEntry[] {
        if (this.loaded) return this.procedures;

        try {
            if (!fs.existsSync(this.procedureDir)) {
                this.loaded = true;
                return this.procedures;
            }

            const files = fs.readdirSync(this.procedureDir)
                .filter((f) => f.endsWith('.md'));

            for (const file of files) {
                const filePath = path.join(this.procedureDir, file);
                try {
                    const content = fs.readFileSync(filePath, 'utf-8');
                    const entry = this.parseProcedureFile(content, filePath);
                    if (entry) {
                        this.procedures.push(entry);
                    }
                } catch { /* skip unreadable files */ }
            }

            this.loaded = true;
        } catch { /* directory not accessible */ }

        return this.procedures;
    }

    private parseProcedureFile(content: string, filePath: string): ProceduralEntry | null {
        // Parse YAML frontmatter
        const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
        let name = path.basename(filePath, '.md').replace(/_/g, ' ');
        let triggers: string[] = [];

        if (frontmatterMatch) {
            const yaml = frontmatterMatch[1];

            const nameMatch = yaml.match(/name:\s*(.+)/);
            if (nameMatch) name = nameMatch[1].trim();

            const triggerMatch = yaml.match(/triggers:\s*\[([^\]]+)\]/);
            if (triggerMatch) {
                triggers = triggerMatch[1]
                    .split(',')
                    .map((t) => t.trim().replace(/['"]/g, ''))
                    .filter(Boolean);
            }
        }

        // Extract steps (numbered lines or bullet points)
        const body = frontmatterMatch
            ? content.slice(frontmatterMatch[0].length).trim()
            : content.trim();

        const steps = body
            .split('\n')
            .filter((line) => /^\s*(?:\d+\.|[-*])\s+/.test(line))
            .map((line) => line.replace(/^\s*(?:\d+\.|[-*])\s+/, '').trim());

        if (!name && steps.length === 0) return null;

        // Auto-generate triggers from name if not specified
        if (triggers.length === 0) {
            triggers = name
                .toLowerCase()
                .split(/[\s_-]+/)
                .filter((t) => t.length >= 2);
        }

        return {
            id: path.basename(filePath, '.md'),
            name,
            triggerPatterns: triggers,
            steps: steps.length > 0 ? steps : [body.slice(0, 500)],
            filePath,
            lastUsed: 0,
            useCount: 0
        };
    }

    // ─── Matching ───

    /**
     * 프롬프트에 매칭되는 절차를 찾아 반환합니다.
     */
    public findMatchingProcedures(prompt: string): ProceduralEntry[] {
        this.loadProcedures();
        const promptLower = prompt.toLowerCase();

        return this.procedures.filter((proc) =>
            proc.triggerPatterns.some((trigger) => promptLower.includes(trigger.toLowerCase()))
        );
    }

    // ─── Context Building ───

    public buildContext(currentPrompt: string): MemoryContextResult | null {
        const matches = this.findMatchingProcedures(currentPrompt);
        if (matches.length === 0) return null;

        // Mark as used
        for (const proc of matches) {
            proc.lastUsed = Date.now();
            proc.useCount++;
        }

        const content = matches
            .map((proc) => {
                const stepsText = proc.steps.length > 0
                    ? proc.steps.map((s, i) => `  ${i + 1}. ${s}`).join('\n')
                    : '  (절차 상세 없음)';
                return `📋 ${proc.name}\n${stepsText}`;
            })
            .join('\n\n');

        return {
            layer: 'procedural',
            label: 'Procedural Memory (반복 작업 절차)',
            content,
            relevance: 0.9
        };
    }

    // ─── Utility ───

    public getAllProcedures(): ProceduralEntry[] {
        this.loadProcedures();
        return [...this.procedures];
    }
}
