"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.shareCommunityPattern = shareCommunityPattern;
exports.getCommunityPatterns = getCommunityPatterns;
exports.voteCommunityPattern = voteCommunityPattern;
exports.incrementUsage = incrementUsage;
exports.addCommunityInsight = addCommunityInsight;
exports.getCommunityInsights = getCommunityInsights;
exports.addBenchmark = addBenchmark;
exports.getBenchmarks = getBenchmarks;
exports.getCommunityStats = getCommunityStats;
exports.getTechEvolution = getTechEvolution;
exports.getFailurePatterns = getFailurePatterns;
exports.validatePrivacy = validatePrivacy;
exports.createAnonymousId = createAnonymousId;
const database_1 = require("../database/database");
const crypto_1 = require("crypto");
// ─── Privacy-safe hashing ─────────────────────────────────────────────────
function anonymousHash(input) {
    return (0, crypto_1.createHash)('sha256').update(input + 'cell-anon-salt-v1').digest('hex').slice(0, 16);
}
let _initialized = false;
function ensureInit() {
    if (_initialized)
        return;
    (0, database_1.initializeDatabase)();
    _initialized = true;
}
function sanitizeRule(rule) {
    // Strip anything that looks like code, file paths, or PII
    return rule
        .replace(/\b\w+\.(ts|js|tsx|jsx|py|java|go|rs|cpp|c)\b/gi, '<file>')
        .replace(/(['"])(?:\\.|[^\\])*?\1/g, '<string>')
        .replace(/\b\d+\b/g, '<num>')
        .slice(0, 200);
}
// ─── Patterns CRUD ───────────────────────────────────────────────────────
function shareCommunityPattern(params) {
    const db = (0, database_1.getDb)();
    const warnings = [];
    const sanitized = sanitizeRule(params.rule);
    if (sanitized !== params.rule) {
        warnings.push('Rule was sanitized — code/file paths/numbers removed');
    }
    // PII detection
    const piiCheck = /\b[A-Z][a-z]+ [A-Z][a-z]+\b/.test(params.rule);
    if (piiCheck) {
        warnings.push('Possible name detected — will be anonymized');
    }
    const anonId = params.anonymousId || anonymousHash(`user-${Date.now()}-${Math.random()}`);
    const patternId = `cp-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
    const stmt = db.prepare(`
    INSERT INTO community_patterns (pattern_id, anonymous_id, category, language, framework, rule, success_rate, usage_count, votes, privacy_safe)
    VALUES (?, ?, ?, ?, ?, ?, ?, 1, 0, 1)
  `);
    const result = stmt.run(patternId, anonId, params.category, params.language || '', params.framework || '', sanitized, params.successRate || 0);
    return { id: Number(result.lastInsertRowid), patternId, privacySafe: true, warnings };
}
function getCommunityPatterns(filters) {
    const db = (0, database_1.getDb)();
    let sql = 'SELECT * FROM community_patterns WHERE privacy_safe = 1';
    const args = [];
    if (filters?.category) {
        sql += ' AND category = ?';
        args.push(filters.category);
    }
    if (filters?.language) {
        sql += ' AND language = ?';
        args.push(filters.language);
    }
    if (filters?.minVotes !== undefined) {
        sql += ' AND votes >= ?';
        args.push(filters.minVotes);
    }
    sql += ' ORDER BY votes DESC, success_rate DESC LIMIT ?';
    args.push(filters?.limit || 20);
    const rows = db.prepare(sql).all(...args);
    return rows.map(mapPattern);
}
function voteCommunityPattern(patternId, upvote) {
    const db = (0, database_1.getDb)();
    const delta = upvote ? 1 : -1;
    const result = db.prepare('UPDATE community_patterns SET votes = votes + ? WHERE pattern_id = ?').run(delta, patternId);
    if (result.changes === 0)
        return { success: false, newVotes: 0 };
    const row = db.prepare('SELECT votes FROM community_patterns WHERE pattern_id = ?').get(patternId);
    return { success: true, newVotes: row?.votes || 0 };
}
function incrementUsage(patternId) {
    const db = (0, database_1.getDb)();
    const result = db.prepare('UPDATE community_patterns SET usage_count = usage_count + 1 WHERE pattern_id = ?').run(patternId);
    if (result.changes === 0)
        return { success: false, newCount: 0 };
    const row = db.prepare('SELECT usage_count FROM community_patterns WHERE pattern_id = ?').get(patternId);
    return { success: true, newCount: row?.usage_count || 0 };
}
function mapPattern(r) {
    return {
        id: Number(r.id),
        patternId: String(r.pattern_id),
        anonymousId: String(r.anonymous_id),
        category: String(r.category || ''),
        language: String(r.language || ''),
        framework: String(r.framework || ''),
        rule: String(r.rule || ''),
        successRate: Number(r.success_rate || 0),
        usageCount: Number(r.usage_count || 0),
        votes: Number(r.votes || 0),
        privacySafe: r.privacy_safe === 1,
        sharedAt: String(r.shared_at || ''),
    };
}
// ─── Insights CRUD ────────────────────────────────────────────────────────
function addCommunityInsight(insight) {
    const db = (0, database_1.getDb)();
    const result = db.prepare(`
    INSERT INTO community_insights (insight_type, title, description, category, language, severity, evidence)
    VALUES (?, ?, ?, ?, ?, ?, ?)
  `).run(insight.type, insight.title, insight.description || '', insight.category || '', insight.language || '', insight.severity, insight.evidence || '');
    return { id: Number(result.lastInsertRowid) };
}
function getCommunityInsights(filters) {
    const db = (0, database_1.getDb)();
    let sql = 'SELECT * FROM community_insights WHERE 1=1';
    const args = [];
    if (filters?.severity) {
        sql += ' AND severity = ?';
        args.push(filters.severity);
    }
    if (filters?.category) {
        sql += ' AND category = ?';
        args.push(filters.category);
    }
    sql += ' ORDER BY created_at DESC LIMIT ?';
    args.push(filters?.limit || 20);
    const rows = db.prepare(sql).all(...args);
    return rows.map((r) => ({
        id: Number(r.id),
        type: String(r.insight_type || ''),
        title: String(r.title || ''),
        description: String(r.description || ''),
        category: String(r.category || ''),
        language: String(r.language || ''),
        severity: String(r.severity || 'info'),
        evidence: String(r.evidence || ''),
        createdAt: String(r.created_at || ''),
    }));
}
// ─── Benchmarks CRUD ─────────────────────────────────────────────────────
function addBenchmark(params) {
    const db = (0, database_1.getDb)();
    const delta = params.userValue !== undefined ? params.userValue - params.value : 0;
    const result = db.prepare(`
    INSERT INTO community_benchmarks (metric, value, user_value, delta, category)
    VALUES (?, ?, ?, ?, ?)
  `).run(params.metric, params.value, params.userValue ?? null, delta, params.category || '');
    return { id: Number(result.lastInsertRowid), delta };
}
function getBenchmarks(filters) {
    const db = (0, database_1.getDb)();
    let sql = 'SELECT * FROM community_benchmarks WHERE 1=1';
    const args = [];
    if (filters?.category) {
        sql += ' AND category = ?';
        args.push(filters.category);
    }
    sql += ' ORDER BY created_at DESC LIMIT ?';
    args.push(filters?.limit || 50);
    const rows = db.prepare(sql).all(...args);
    return rows.map((r) => ({
        id: Number(r.id),
        metric: String(r.metric || ''),
        value: Number(r.value || 0),
        userValue: Number(r.user_value || 0),
        delta: Number(r.delta || 0),
        category: String(r.category || ''),
        createdAt: String(r.created_at || ''),
    }));
}
// ─── Aggregated stats ─────────────────────────────────────────────────────
function getCommunityStats() {
    const db = (0, database_1.getDb)();
    const totalPatterns = db.prepare('SELECT COUNT(*) as c FROM community_patterns').get().c;
    const totalInsights = db.prepare('SELECT COUNT(*) as c FROM community_insights').get().c;
    const totalBenchmarks = db.prepare('SELECT COUNT(*) as c FROM community_benchmarks').get().c;
    const totalVotes = db.prepare('SELECT COALESCE(SUM(votes), 0) as s FROM community_patterns').get().s;
    const byCategoryRows = db.prepare('SELECT category, COUNT(*) as c FROM community_patterns GROUP BY category').all();
    const byLanguageRows = db.prepare("SELECT language, COUNT(*) as c FROM community_patterns WHERE language != '' GROUP BY language").all();
    const topPatterns = db.prepare('SELECT * FROM community_patterns ORDER BY votes DESC LIMIT 5').all().map(mapPattern);
    const topInsights = getCommunityInsights({ severity: 'critical', limit: 5 });
    // Recent activity
    const recentPatterns = db.prepare('SELECT COUNT(*) as c, MAX(shared_at) as last FROM community_patterns').get();
    const recentInsights = db.prepare('SELECT COUNT(*) as c, MAX(created_at) as last FROM community_insights').get();
    return {
        totalPatterns,
        totalInsights,
        totalBenchmarks,
        totalVotes,
        byCategory: Object.fromEntries(byCategoryRows.map(r => [r.category, r.c])),
        byLanguage: Object.fromEntries(byLanguageRows.map(r => [r.language, r.c])),
        topPatterns,
        topInsights,
        recentActivity: [
            { type: 'patterns', count: recentPatterns.c, lastAt: recentPatterns.last || '' },
            { type: 'insights', count: recentInsights.c, lastAt: recentInsights.last || '' },
        ],
    };
}
// ─── Tech evolution tracking (5.5) ───────────────────────────────────────
function getTechEvolution() {
    const db = (0, database_1.getDb)();
    // Look at tech_proficiency + community_signals to derive adoption phases
    const proficiency = db.prepare(`
    SELECT technology, signals_count, proficiency_score, last_seen
    FROM tech_proficiency
    ORDER BY proficiency_score DESC
  `).all();
    return proficiency.map((p) => {
        const signals = p.signals_count;
        const score = p.proficiency_score;
        let phase = 'mature';
        let trend30d = 0;
        let trend90d = 0;
        if (signals < 5)
            phase = 'emerging';
        else if (signals < 20)
            phase = 'growing';
        else if (score > 70)
            phase = 'mature';
        // Simple heuristic: more signals = upward trend; fewer = downward
        if (signals > 100)
            trend30d = 15;
        else if (signals > 50)
            trend30d = 8;
        else if (signals > 20)
            trend30d = 3;
        else if (signals < 5)
            trend30d = -2;
        trend90d = trend30d * 2;
        return {
            technology: p.technology,
            currentUsage: signals,
            trend30d,
            trend90d,
            adoptionPhase: phase,
            relatedMigrations: getMigrationsFor(p.technology),
        };
    });
}
function getMigrationsFor(tech) {
    const db = (0, database_1.getDb)();
    const rows = db.prepare(`
    SELECT DISTINCT technology FROM tech_proficiency
    WHERE technology != ? AND category = (SELECT category FROM tech_proficiency WHERE technology = ? LIMIT 1)
    LIMIT 3
  `).all(tech, tech);
    return rows.map(r => r.technology);
}
// ─── Failure pattern library (5.3) ───────────────────────────────────────
function getFailurePatterns(filters) {
    const db = (0, database_1.getDb)();
    // Mine error_log + community_insights for failure patterns
    let errorSql = 'SELECT error_type, COUNT(*) as freq FROM error_log';
    const args = [];
    if (filters?.language) {
        errorSql += ' WHERE project = ?';
        args.push(filters.language);
    }
    errorSql += ' GROUP BY error_type ORDER BY freq DESC LIMIT ?';
    args.push(filters?.limit || 10);
    const errors = db.prepare(errorSql).all(...args);
    return errors.map(e => {
        const severity = e.freq > 5 ? 'critical' : e.freq > 2 ? 'warning' : 'info';
        return {
            category: String(e.error_type || 'unknown'),
            language: filters?.language || '',
            description: `${e.error_type} occurred ${e.freq} times in the community`,
            frequency: e.freq,
            severity,
            mitigation: getMitigationFor(String(e.error_type)),
        };
    });
}
function getMitigationFor(errorType) {
    const m = {
        'null_check': 'Always validate inputs at function entry; use optional chaining or guards',
        'validation': 'Add Zod/Joi/Yup schemas for all user-facing APIs',
        'n_plus_1': 'Use batch queries or eager loading; check with EXPLAIN',
        'async_error': 'Wrap awaits in try/catch; never swallow promise rejections',
        'sql_injection': 'Use parameterized queries exclusively; never concat user input',
        'race_condition': 'Use mutexes/locks; serialize critical sections',
        'memory_leak': 'Profile with heap snapshots; clear event listeners',
    };
    return m[errorType] || `Review top Stack Overflow answers for "${errorType}"`;
}
function validatePrivacy(rule) {
    const errors = [];
    const warnings = [];
    const sanitized = sanitizeRule(rule);
    // Check for code-looking content
    if (/\bfunction\s+\w+\s*\(/.test(rule)) {
        warnings.push('Contains function definitions — will be abstracted');
    }
    if (/\b(import|export)\s+/.test(rule)) {
        warnings.push('Contains import/export — will be removed');
    }
    if (/\b(const|let|var)\s+\w+\s*=/.test(rule)) {
        warnings.push('Contains variable declarations — will be removed');
    }
    if (/\b[A-Z][a-z]+ [A-Z][a-z]+/.test(rule)) {
        warnings.push('Possible name detected — will be anonymized');
    }
    if (/@[\w.]+/.test(rule)) {
        errors.push('Contains email-like pattern — BLOCKED');
    }
    if (/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/.test(rule)) {
        errors.push('Contains IP address — BLOCKED');
    }
    return {
        safe: errors.length === 0,
        errors,
        warnings,
        sanitized,
    };
}
function createAnonymousId() {
    return anonymousHash(`user-${Date.now()}-${Math.random()}`);
}
//# sourceMappingURL=community-store.js.map