/**
 * Interpreter Service
 *
 * Open Interpreter-inspired service for Code Buddy.
 * Manages profiles, safe mode, auto-run, and budget tracking.
 *
 * Usage:
 * ```typescript
 * import { interpreter } from './services/InterpreterService';
 *
 * // Load a profile
 * interpreter.loadProfile('fast');
 *
 * // Configure
 * interpreter.autoRun = true;
 * interpreter.safeMode = 'ask';
 * interpreter.maxBudget = 1.00;
 *
 * // Execute
 * const result = await interpreter.chat('Open Chrome');
 * await interpreter.continue();
 * interpreter.reset();
 *
 * // Stats
 * console.log(interpreter.tokenUsage);
 * console.log(interpreter.totalCost);
 * ```
 */
import { EventEmitter } from 'events';
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import * as yaml from 'yaml';
import { DEFAULT_INTERPRETER_CONFIG, DEFAULT_MODEL_PRICING, } from './types.js';
import { DEFAULT_PROFILE, BUILTIN_PROFILE_MAP, getBuiltinProfile, validateProfile, } from './profiles.js';
// ============================================================================
// Interpreter Service Class
// ============================================================================
export class InterpreterService extends EventEmitter {
    config;
    state;
    conversationHistory = [];
    customProfiles = new Map();
    pendingApprovals = new Map();
    constructor(config = {}) {
        super();
        this.config = { ...DEFAULT_INTERPRETER_CONFIG, ...config };
        this.state = this.createInitialState();
        this.loadCustomProfiles();
        this.loadPersistedUsage();
    }
    // ==========================================================================
    // Public Properties
    // ==========================================================================
    /** Get current profile */
    get profile() {
        return this.state.activeProfile;
    }
    /** Get/set auto-run mode */
    get autoRun() {
        return this.state.autoRun;
    }
    set autoRun(value) {
        this.state.autoRun = value;
        this.emit('autoRun:changed', value);
    }
    /** Get/set safe mode */
    get safeMode() {
        return this.state.safeMode;
    }
    set safeMode(value) {
        this.state.safeMode = value;
        this.emit('safeMode:changed', value);
    }
    /** Get/set custom instructions */
    get customInstructions() {
        return this.state.customInstructions;
    }
    set customInstructions(value) {
        this.state.customInstructions = value;
    }
    /** Get/set max budget */
    get maxBudget() {
        return this.state.usage.budget.maxBudget;
    }
    set maxBudget(value) {
        this.state.usage.budget.maxBudget = value;
        this.updateBudgetStatus();
    }
    /** Get token usage */
    get tokenUsage() {
        return { ...this.state.usage.tokens };
    }
    /** Get total cost */
    get totalCost() {
        return this.state.usage.cost.totalCost;
    }
    /** Get budget status */
    get budgetStatus() {
        return { ...this.state.usage.budget };
    }
    /** Get full usage stats */
    get usage() {
        return { ...this.state.usage };
    }
    /** Is currently processing */
    get isProcessing() {
        return this.state.isProcessing;
    }
    /** Is loop mode enabled */
    get loopMode() {
        return this.state.loopMode;
    }
    set loopMode(value) {
        this.state.loopMode = value;
    }
    // ==========================================================================
    // Profile Management
    // ==========================================================================
    /**
     * Load a profile by ID
     */
    loadProfile(profileId) {
        // Check built-in profiles first
        let profile = getBuiltinProfile(profileId);
        // Then check custom profiles
        if (!profile) {
            profile = this.customProfiles.get(profileId);
        }
        if (!profile) {
            throw new Error(`Profile not found: ${profileId}`);
        }
        this.state.activeProfile = profile;
        this.state.autoRun = profile.autoRun;
        this.state.safeMode = profile.safeMode;
        this.state.customInstructions = profile.customInstructions;
        if (profile.maxBudget !== undefined) {
            this.state.usage.budget.maxBudget = profile.maxBudget;
            this.updateBudgetStatus();
        }
        this.emit('profile:changed', profile);
    }
    /**
     * Load a profile from YAML file
     */
    loadProfileFromFile(filePath) {
        const resolvedPath = this.resolvePath(filePath);
        if (!fs.existsSync(resolvedPath)) {
            throw new Error(`Profile file not found: ${resolvedPath}`);
        }
        const content = fs.readFileSync(resolvedPath, 'utf-8');
        const profile = yaml.parse(content);
        const validation = validateProfile(profile);
        if (!validation.valid) {
            throw new Error(`Invalid profile: ${validation.errors.join(', ')}`);
        }
        this.customProfiles.set(profile.id, profile);
        this.loadProfile(profile.id);
    }
    /**
     * Save current profile to YAML file
     */
    saveProfile(filePath) {
        const resolvedPath = this.resolvePath(filePath);
        const dir = path.dirname(resolvedPath);
        if (!fs.existsSync(dir)) {
            fs.mkdirSync(dir, { recursive: true });
        }
        const content = yaml.stringify(this.state.activeProfile);
        fs.writeFileSync(resolvedPath, content, 'utf-8');
    }
    /**
     * List available profiles
     */
    listProfiles() {
        const profiles = [];
        // Add built-in profiles
        for (const [id, profile] of BUILTIN_PROFILE_MAP) {
            profiles.push({
                id,
                name: profile.name,
                description: profile.description,
                builtin: true,
            });
        }
        // Add custom profiles
        for (const [id, profile] of this.customProfiles) {
            profiles.push({
                id,
                name: profile.name,
                description: profile.description,
                builtin: false,
            });
        }
        return profiles;
    }
    /**
     * Create a custom profile
     */
    createProfile(profile) {
        const validation = validateProfile(profile);
        if (!validation.valid) {
            throw new Error(`Invalid profile: ${validation.errors.join(', ')}`);
        }
        this.customProfiles.set(profile.id, profile);
        // Persist to disk
        const profilePath = path.join(this.resolvePath(this.config.profilesDir), `${profile.id}.yaml`);
        const dir = path.dirname(profilePath);
        if (!fs.existsSync(dir)) {
            fs.mkdirSync(dir, { recursive: true });
        }
        fs.writeFileSync(profilePath, yaml.stringify(profile), 'utf-8');
    }
    /**
     * Delete a custom profile
     */
    deleteProfile(profileId) {
        if (BUILTIN_PROFILE_MAP.has(profileId)) {
            throw new Error('Cannot delete built-in profile');
        }
        const deleted = this.customProfiles.delete(profileId);
        if (deleted) {
            const profilePath = path.join(this.resolvePath(this.config.profilesDir), `${profileId}.yaml`);
            if (fs.existsSync(profilePath)) {
                fs.unlinkSync(profilePath);
            }
        }
        return deleted;
    }
    // ==========================================================================
    // Chat Interface
    // ==========================================================================
    /**
     * Send a message and get a response
     */
    async chat(message) {
        if (this.state.isProcessing) {
            throw new Error('Already processing a request');
        }
        // Check budget
        if (this.state.usage.budget.exceeded) {
            throw new Error('Budget exceeded. Reset usage or increase budget.');
        }
        this.state.isProcessing = true;
        try {
            // Add user message to history
            const userMessage = {
                role: 'user',
                content: message,
                timestamp: new Date(),
            };
            this.conversationHistory.push(userMessage);
            // TODO: Integrate with actual LLM client
            // For now, return a placeholder result
            const result = await this.processMessage(message);
            // Update usage
            this.updateUsage(result.tokens, result.cost);
            // Add assistant message to history
            const assistantMessage = {
                role: 'assistant',
                content: result.content,
                timestamp: new Date(),
                tokenCount: result.tokens.output,
                cost: result.cost,
                toolCalls: result.toolCalls,
                toolResults: result.toolResults,
            };
            this.conversationHistory.push(assistantMessage);
            return result;
        }
        finally {
            this.state.isProcessing = false;
        }
    }
    /**
     * Continue the conversation (loop mode)
     */
    async continue() {
        if (!this.state.loopMode) {
            return null;
        }
        // Get last assistant message
        const lastMessage = this.conversationHistory[this.conversationHistory.length - 1];
        if (lastMessage?.role !== 'assistant') {
            return null;
        }
        // Check if there are pending tool calls
        if (lastMessage.toolCalls && lastMessage.toolCalls.length > 0) {
            // Continue with tool execution
            return this.chat('Continue with the task.');
        }
        return null;
    }
    /**
     * Reset the conversation
     */
    reset() {
        this.conversationHistory = [];
        this.pendingApprovals.clear();
        this.state.conversationId = undefined;
        this.state.loopMode = false;
    }
    /**
     * Get conversation history
     */
    getHistory() {
        return [...this.conversationHistory];
    }
    // ==========================================================================
    // Approval System
    // ==========================================================================
    /**
     * Check if an action needs approval
     */
    needsApproval(action, risk) {
        switch (this.state.safeMode) {
            case 'off':
                return false;
            case 'ask':
                return true;
            case 'auto':
                return risk === 'high' || risk === 'critical';
            default:
                return true;
        }
    }
    /**
     * Request approval for an action
     */
    requestApproval(approval) {
        const id = `approval-${Date.now()}-${Math.random().toString(36).slice(2)}`;
        const pending = { ...approval, id };
        this.pendingApprovals.set(id, pending);
        this.emit('approval:needed', pending);
        return id;
    }
    /**
     * Approve a pending action
     */
    async approve(approvalId) {
        const pending = this.pendingApprovals.get(approvalId);
        if (!pending) {
            throw new Error(`Approval not found: ${approvalId}`);
        }
        this.pendingApprovals.delete(approvalId);
        const result = await pending.action();
        this.emit('tool:executed', pending.description, result);
        return result;
    }
    /**
     * Reject a pending action
     */
    reject(approvalId) {
        this.pendingApprovals.delete(approvalId);
    }
    /**
     * Get all pending approvals
     */
    getPendingApprovals() {
        return Array.from(this.pendingApprovals.values());
    }
    // ==========================================================================
    // Usage & Budget
    // ==========================================================================
    /**
     * Update usage statistics
     */
    updateUsage(tokens, cost) {
        this.state.usage.tokens.input += tokens.input;
        this.state.usage.tokens.output += tokens.output;
        this.state.usage.tokens.total += tokens.total;
        if (tokens.cached) {
            this.state.usage.tokens.cached = (this.state.usage.tokens.cached || 0) + tokens.cached;
        }
        this.state.usage.cost.totalCost += cost;
        this.state.usage.requestCount++;
        this.state.usage.lastRequest = new Date();
        this.updateBudgetStatus();
        this.emit('usage:updated', this.state.usage);
        // Persist usage
        if (this.config.persistUsage) {
            this.persistUsage();
        }
    }
    /**
     * Update budget status
     */
    updateBudgetStatus() {
        const budget = this.state.usage.budget;
        budget.currentSpend = this.state.usage.cost.totalCost;
        budget.remaining = Math.max(0, budget.maxBudget - budget.currentSpend);
        budget.percentUsed = budget.maxBudget > 0
            ? (budget.currentSpend / budget.maxBudget) * 100
            : 0;
        budget.exceeded = budget.currentSpend >= budget.maxBudget && budget.maxBudget > 0;
        // Emit warnings
        if (budget.percentUsed >= 80 && budget.percentUsed < 100) {
            this.emit('budget:warning', budget);
        }
        else if (budget.exceeded) {
            this.emit('budget:exceeded', budget);
        }
    }
    /**
     * Reset usage statistics
     */
    resetUsage() {
        this.state.usage = this.createInitialUsage();
        if (this.config.persistUsage && this.config.usagePath) {
            const usagePath = this.resolvePath(this.config.usagePath);
            if (fs.existsSync(usagePath)) {
                fs.unlinkSync(usagePath);
            }
        }
    }
    /**
     * Calculate cost for tokens
     */
    calculateCost(model, tokens) {
        const pricing = this.config.pricing[model] || DEFAULT_MODEL_PRICING['grok-3-mini'];
        const inputCost = (tokens.input / 1_000_000) * pricing.input;
        const outputCost = (tokens.output / 1_000_000) * pricing.output;
        return inputCost + outputCost;
    }
    /**
     * Format usage for display
     */
    formatUsage() {
        const { tokens, cost, budget } = this.state.usage;
        const lines = [
            '📊 Usage Statistics',
            '',
            `Tokens: ${tokens.total.toLocaleString()} (${tokens.input.toLocaleString()} in / ${tokens.output.toLocaleString()} out)`,
            `Cost: $${cost.totalCost.toFixed(4)}`,
            `Budget: $${budget.currentSpend.toFixed(4)} / $${budget.maxBudget.toFixed(2)} (${budget.percentUsed.toFixed(1)}%)`,
            `Requests: ${this.state.usage.requestCount}`,
        ];
        if (budget.exceeded) {
            lines.push('', '⚠️ Budget exceeded!');
        }
        else if (budget.percentUsed >= 80) {
            lines.push('', '⚠️ Budget warning: 80% used');
        }
        return lines.join('\n');
    }
    // ==========================================================================
    // Private Methods
    // ==========================================================================
    createInitialState() {
        return {
            activeProfile: DEFAULT_PROFILE,
            autoRun: DEFAULT_PROFILE.autoRun,
            safeMode: DEFAULT_PROFILE.safeMode,
            customInstructions: DEFAULT_PROFILE.customInstructions,
            usage: this.createInitialUsage(),
            isProcessing: false,
            loopMode: false,
        };
    }
    createInitialUsage() {
        return {
            tokens: { input: 0, output: 0, total: 0 },
            cost: { inputCost: 0, outputCost: 0, totalCost: 0, currency: 'USD' },
            budget: {
                maxBudget: this.config.globalMaxBudget || 10.00,
                currentSpend: 0,
                remaining: this.config.globalMaxBudget || 10.00,
                percentUsed: 0,
                exceeded: false,
            },
            requestCount: 0,
            sessionStart: new Date(),
        };
    }
    resolvePath(p) {
        if (p.startsWith('~')) {
            return path.join(os.homedir(), p.slice(1));
        }
        return path.resolve(p);
    }
    loadCustomProfiles() {
        const profilesDir = this.resolvePath(this.config.profilesDir);
        if (!fs.existsSync(profilesDir)) {
            return;
        }
        const files = fs.readdirSync(profilesDir);
        for (const file of files) {
            if (file.endsWith('.yaml') || file.endsWith('.yml')) {
                try {
                    const content = fs.readFileSync(path.join(profilesDir, file), 'utf-8');
                    const profile = yaml.parse(content);
                    const validation = validateProfile(profile);
                    if (validation.valid) {
                        this.customProfiles.set(profile.id, profile);
                    }
                }
                catch {
                    // Skip invalid profiles
                }
            }
        }
    }
    loadPersistedUsage() {
        if (!this.config.persistUsage || !this.config.usagePath) {
            return;
        }
        const usagePath = this.resolvePath(this.config.usagePath);
        if (!fs.existsSync(usagePath)) {
            return;
        }
        try {
            const content = fs.readFileSync(usagePath, 'utf-8');
            const data = JSON.parse(content);
            // Merge persisted usage
            this.state.usage.tokens = data.tokens || this.state.usage.tokens;
            this.state.usage.cost = data.cost || this.state.usage.cost;
            this.state.usage.requestCount = data.requestCount || 0;
            this.state.usage.sessionStart = new Date(data.sessionStart || Date.now());
            this.updateBudgetStatus();
        }
        catch {
            // Ignore errors, start fresh
        }
    }
    persistUsage() {
        if (!this.config.usagePath) {
            return;
        }
        const usagePath = this.resolvePath(this.config.usagePath);
        const dir = path.dirname(usagePath);
        if (!fs.existsSync(dir)) {
            fs.mkdirSync(dir, { recursive: true });
        }
        const data = {
            tokens: this.state.usage.tokens,
            cost: this.state.usage.cost,
            requestCount: this.state.usage.requestCount,
            sessionStart: this.state.usage.sessionStart.toISOString(),
            lastRequest: this.state.usage.lastRequest?.toISOString(),
        };
        fs.writeFileSync(usagePath, JSON.stringify(data, null, 2), 'utf-8');
    }
    async processMessage(_message) {
        // TODO: Integrate with actual LLM client
        // This is a placeholder implementation
        return {
            content: 'Processing message...',
            tokens: { input: 100, output: 50, total: 150 },
            cost: 0.001,
            autoApproved: this.state.autoRun,
        };
    }
}
// ============================================================================
// Singleton
// ============================================================================
let interpreterInstance = null;
export function getInterpreter(config) {
    if (!interpreterInstance) {
        interpreterInstance = new InterpreterService(config);
    }
    return interpreterInstance;
}
export function resetInterpreter() {
    interpreterInstance = null;
}
// Default export for convenience
export const interpreter = getInterpreter();
export default InterpreterService;
//# sourceMappingURL=interpreter-service.js.map