/**
 * AnalyticsTracker — Singleton analytics engine for NodeBench MCP
 *
 * Owns the full lifecycle: DB open, tool-call recording, project context,
 * session stats, retention cleanup, and graceful shutdown.
 * index.ts calls tracker.record() instead of inline SQL.
 */
import { getAnalyticsDb, closeAnalyticsDb, updateProjectContext, recordPresetSelection, clearOldRecords, } from './schema.js';
import { detectProject } from './projectDetector.js';
// ── Singleton ───────────────────────────────────────────────────────────
let _instance = null;
export class AnalyticsTracker {
    db;
    projectPath;
    preset;
    toolToToolset;
    skipTools;
    sessionStart;
    _callCount = 0;
    _insertStmt = null;
    constructor(config) {
        this.db = getAnalyticsDb();
        this.projectPath = config.projectPath;
        this.preset = config.preset;
        this.toolToToolset = config.toolToToolset;
        this.skipTools = config.skipTools ?? new Set();
        this.sessionStart = Date.now();
        // Run retention cleanup once on startup (non-blocking, best-effort)
        const retentionDays = config.retentionDays ?? 90;
        try {
            clearOldRecords(this.db, retentionDays);
        }
        catch { /* ignore */ }
        // Register project context
        this._initProjectContext(config);
        // Pre-compile the insert statement for performance
        this._insertStmt = this.db.prepare(`INSERT INTO tool_usage (tool_name, toolset, timestamp, duration, success, error_message, project_path, preset, args)
       VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`);
    }
    /** Get or create the singleton tracker */
    static init(config) {
        if (!_instance) {
            _instance = new AnalyticsTracker(config);
        }
        return _instance;
    }
    /** Get existing instance (returns null if not initialized) */
    static get() {
        return _instance;
    }
    /** Underlying DB handle (for stats queries that need it) */
    getDb() {
        return this.db;
    }
    // ── Core: record a tool call ────────────────────────────────────────
    /**
     * Record a tool call outcome. Called from the CallToolRequestSchema handler.
     * Returns silently on any error — analytics must never break tool dispatch.
     */
    record(toolName, startMs, success, errorMsg, args) {
        if (this.skipTools.has(toolName))
            return;
        try {
            const toolset = this.toolToToolset.get(toolName) ?? 'unknown';
            this._insertStmt.run(toolName, toolset, startMs, Date.now() - startMs, success ? 1 : 0, errorMsg, this.projectPath, this.preset, args ? JSON.stringify(args) : null);
            this._callCount++;
        }
        catch { /* never break tool dispatch */ }
    }
    // ── Session stats ───────────────────────────────────────────────────
    getSessionStats() {
        const stmt = this.db.prepare(`
      SELECT
        COUNT(*) as totalCalls,
        SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) as successCount,
        SUM(CASE WHEN success = 0 THEN 1 ELSE 0 END) as failureCount,
        SUM(duration) as totalDuration,
        COUNT(DISTINCT tool_name) as uniqueTools
      FROM tool_usage
      WHERE project_path = ? AND timestamp >= ?
    `);
        const r = stmt.get(this.projectPath, this.sessionStart);
        const topStmt = this.db.prepare(`
      SELECT tool_name as name, COUNT(*) as count
      FROM tool_usage
      WHERE project_path = ? AND timestamp >= ?
      GROUP BY tool_name ORDER BY count DESC LIMIT 5
    `);
        const topTools = topStmt.all(this.projectPath, this.sessionStart);
        const total = r.totalCalls || 0;
        const failures = r.failureCount || 0;
        return {
            totalCalls: total,
            successCount: r.successCount || 0,
            failureCount: failures,
            totalDuration: r.totalDuration || 0,
            uniqueTools: r.uniqueTools || 0,
            topTools,
            errorRate: total > 0 ? failures / total : 0,
        };
    }
    get callCount() {
        return this._callCount;
    }
    // ── Lifecycle ───────────────────────────────────────────────────────
    close() {
        try {
            closeAnalyticsDb(this.db);
        }
        catch { /* ignore */ }
        _instance = null;
    }
    // ── Private helpers ─────────────────────────────────────────────────
    _initProjectContext(config) {
        const context = detectProject(config.projectPath);
        const now = Date.now();
        updateProjectContext(this.db, {
            projectPath: config.projectPath,
            projectType: context.projectType,
            detectedAt: now,
            lastSeen: now,
            language: context.language,
            framework: context.framework,
            hasTests: context.hasTests,
            hasCI: context.hasCI,
            hasDocs: context.hasDocs,
            fileCount: context.fileCount,
        });
        recordPresetSelection(this.db, {
            projectPath: config.projectPath,
            preset: config.preset,
            toolsetCount: config.toolCount,
            selectedAt: now,
            selectionReason: 'manual',
        });
    }
}
// ── Convenience re-exports for backward compat ────────────────────────
/** @deprecated Use AnalyticsTracker.init() instead */
export function initializeProjectContext(db, projectPath = process.cwd(), preset, toolCount = 44) {
    const context = detectProject(projectPath);
    const now = Date.now();
    updateProjectContext(db, {
        projectPath,
        projectType: context.projectType,
        detectedAt: now,
        lastSeen: now,
        language: context.language,
        framework: context.framework,
        hasTests: context.hasTests,
        hasCI: context.hasCI,
        hasDocs: context.hasDocs,
        fileCount: context.fileCount,
    });
    recordPresetSelection(db, {
        projectPath,
        preset,
        toolsetCount: toolCount,
        selectedAt: now,
        selectionReason: 'manual',
    });
}
//# sourceMappingURL=toolTracker.js.map