import Database from 'better-sqlite3';
import fs from 'node:fs';
import { getLogger } from '@timmeck/brain-core/utils/logger';
// ── Constants ───────────────────────────────────────
const SIGNAL_LEVELS = ['noise', 'watch', 'signal', 'breakout'];
function signalLevelIndex(level) {
    return SIGNAL_LEVELS.indexOf(level);
}
// ── Importer ────────────────────────────────────────
export class ReposignalImporter {
    brainDb;
    log = getLogger();
    lastResult = null;
    constructor(brainDb) {
        this.brainDb = brainDb;
        this.ensureStateTable();
    }
    ensureStateTable() {
        this.brainDb.exec(`
      CREATE TABLE IF NOT EXISTS reposignal_import_state (
        repo_full_name TEXT PRIMARY KEY,
        signal_score REAL NOT NULL,
        signal_level TEXT NOT NULL,
        imported_at TEXT DEFAULT (datetime('now'))
      )
    `);
    }
    /** Import repos from a reposignal/aisurvival SQLite database. */
    import(dbPath, options = {}) {
        const start = Date.now();
        const minLevel = options.minSignalLevel ?? 'watch';
        const batchSize = options.batchSize ?? 5000;
        const includeHn = options.includeHnMentions !== false;
        const recordMetrics = options.recordMetrics !== false;
        if (!fs.existsSync(dbPath)) {
            throw new Error(`Reposignal DB not found: ${dbPath}`);
        }
        const extDb = new Database(dbPath, { readonly: true });
        const result = {
            dbPath,
            totalReposInDb: 0,
            reposImported: 0,
            discoveriesCreated: 0,
            journalEntriesCreated: 0,
            hnMentionsImported: 0,
            metricsRecorded: 0,
            skippedDuplicates: 0,
            languageBreakdown: {},
            signalBreakdown: {},
            duration_ms: 0,
        };
        try {
            // Count total repos
            result.totalReposInDb = extDb.prepare('SELECT COUNT(*) as c FROM repositories').get().c;
            // Fetch repos at or above minimum signal level
            const minLevelIdx = signalLevelIndex(minLevel);
            const allowedLevels = SIGNAL_LEVELS.filter((_, i) => i >= minLevelIdx);
            const placeholders = allowedLevels.map(() => '?').join(',');
            const repos = extDb.prepare(`
        SELECT id, full_name, name, owner, description, language, topics,
               current_stars, current_forks, signal_score, signal_level, phase,
               first_seen_at, last_scanned_at
        FROM repositories
        WHERE signal_level IN (${placeholders}) AND is_active = 1
        ORDER BY signal_score DESC
        LIMIT ?
      `).all(...allowedLevels, batchSize);
            this.log.info(`[reposignal] Found ${repos.length} repos at ${minLevel}+ level (${result.totalReposInDb} total in DB)`);
            // Check which repos we already imported
            const alreadyImported = new Set();
            const existing = this.brainDb.prepare('SELECT repo_full_name FROM reposignal_import_state').all();
            for (const e of existing)
                alreadyImported.add(e.repo_full_name);
            // Prepare statements
            const insertDiscovery = this.brainDb.prepare(`
        INSERT INTO research_discoveries (type, title, description, confidence, impact, source, data)
        VALUES (?, ?, ?, ?, ?, ?, ?)
      `);
            const insertJournal = this.brainDb.prepare(`
        INSERT INTO research_journal (timestamp, type, title, content, tags, ref_ids, significance, data)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?)
      `);
            const insertState = this.brainDb.prepare(`
        INSERT OR REPLACE INTO reposignal_import_state (repo_full_name, signal_score, signal_level)
        VALUES (?, ?, ?)
      `);
            // Batch import repos
            const importBatch = this.brainDb.transaction(() => {
                for (const repo of repos) {
                    // Track breakdown
                    const lang = repo.language || 'unknown';
                    result.languageBreakdown[lang] = (result.languageBreakdown[lang] || 0) + 1;
                    result.signalBreakdown[repo.signal_level] = (result.signalBreakdown[repo.signal_level] || 0) + 1;
                    // Skip duplicates
                    if (alreadyImported.has(repo.full_name)) {
                        result.skippedDuplicates++;
                        continue;
                    }
                    // Create research discovery for signal+ repos
                    const confidence = Math.min(repo.signal_score / 100, 1.0);
                    const impact = repo.signal_level === 'breakout' ? 0.9 : repo.signal_level === 'signal' ? 0.7 : 0.4;
                    const topics = this.parseTopics(repo.topics);
                    const description = [
                        repo.description || 'No description',
                        `Language: ${lang}`,
                        `Stars: ${repo.current_stars}, Forks: ${repo.current_forks}`,
                        `Phase: ${repo.phase}`,
                        topics.length > 0 ? `Topics: ${topics.join(', ')}` : '',
                    ].filter(Boolean).join('\n');
                    insertDiscovery.run('reposignal_import', `[${repo.signal_level.toUpperCase()}] ${repo.full_name} — ${(repo.description || 'GitHub project').slice(0, 120)}`, description, confidence, impact, 'reposignal', JSON.stringify({
                        repo: repo.full_name,
                        language: lang,
                        stars: repo.current_stars,
                        forks: repo.current_forks,
                        signal_score: repo.signal_score,
                        signal_level: repo.signal_level,
                        phase: repo.phase,
                        topics,
                    }));
                    result.discoveriesCreated++;
                    // Mark as imported
                    insertState.run(repo.full_name, repo.signal_score, repo.signal_level);
                    result.reposImported++;
                }
                // Create journal entries summarizing the import by language
                const langGroups = Object.entries(result.languageBreakdown)
                    .sort(([, a], [, b]) => b - a)
                    .slice(0, 10);
                if (result.reposImported > 0) {
                    const langSummary = langGroups.map(([lang, count]) => `${lang}: ${count}`).join(', ');
                    insertJournal.run(Date.now(), 'discovery', `Reposignal Import: ${result.reposImported} repos imported`, `Imported ${result.reposImported} trending repos from reposignal DB (${result.totalReposInDb} total tracked).\n` +
                        `Signal breakdown: ${Object.entries(result.signalBreakdown).map(([k, v]) => `${k}: ${v}`).join(', ')}\n` +
                        `Top languages: ${langSummary}`, JSON.stringify(['reposignal', 'import', 'tech-trends']), '[]', 'notable', JSON.stringify({ reposImported: result.reposImported, languageBreakdown: result.languageBreakdown }));
                    result.journalEntriesCreated++;
                }
                // Create journal entries for top signal repos (breakout/signal)
                const topRepos = repos
                    .filter(r => !alreadyImported.has(r.full_name) && (r.signal_level === 'breakout' || r.signal_level === 'signal'))
                    .slice(0, 20);
                for (const repo of topRepos) {
                    const topics = this.parseTopics(repo.topics);
                    insertJournal.run(Date.now(), 'discovery', `Trending: ${repo.full_name} (${repo.signal_level}, ${repo.signal_score.toFixed(1)} score)`, `${repo.description || 'No description'}\n` +
                        `Language: ${repo.language || 'unknown'}, Stars: ${repo.current_stars}, Phase: ${repo.phase}`, JSON.stringify(['reposignal', repo.signal_level, repo.language || 'unknown', ...topics.slice(0, 3)]), '[]', repo.signal_level === 'breakout' ? 'breakthrough' : 'notable', JSON.stringify({ repo: repo.full_name, signal_score: repo.signal_score }));
                    result.journalEntriesCreated++;
                }
            });
            importBatch();
            // Import HN mentions
            if (includeHn) {
                result.hnMentionsImported = this.importHnMentions(extDb, insertJournal);
                result.journalEntriesCreated += result.hnMentionsImported;
            }
            // Record metrics for PredictionEngine
            if (recordMetrics) {
                result.metricsRecorded = this.recordImportMetrics(result);
            }
        }
        finally {
            extDb.close();
        }
        result.duration_ms = Date.now() - start;
        this.lastResult = result;
        this.log.info(`[reposignal] Import complete: ${result.reposImported} repos, ${result.discoveriesCreated} discoveries, ${result.journalEntriesCreated} journal entries in ${result.duration_ms}ms`);
        return result;
    }
    /** Import top HN mentions as journal entries. */
    importHnMentions(extDb, insertJournal) {
        let count = 0;
        try {
            const mentions = extDb.prepare(`
        SELECT DISTINCT title, url, score, comment_count
        FROM hn_mentions
        WHERE score > 50
        ORDER BY score DESC
        LIMIT 100
      `).all();
            for (const m of mentions) {
                insertJournal.run(Date.now(), 'discovery', `HN: ${m.title} (${m.score} pts, ${m.comment_count} comments)`, `High-engagement HN/Reddit post: ${m.title}\nScore: ${m.score}, Comments: ${m.comment_count}`, JSON.stringify(['reposignal', 'hn', 'social-signal']), '[]', m.score > 500 ? 'notable' : 'routine', JSON.stringify({ source: 'hn', score: m.score, comments: m.comment_count }));
                count++;
            }
        }
        catch {
            this.log.debug('[reposignal] No HN mentions table or error reading it');
        }
        return count;
    }
    /** Record aggregated metrics into prediction_metrics table. */
    recordImportMetrics(result) {
        let count = 0;
        try {
            const insertMetric = this.brainDb.prepare(`
        INSERT INTO prediction_metrics (metric, value, timestamp, domain)
        VALUES (?, ?, ?, ?)
      `);
            const now = Date.now();
            insertMetric.run('reposignal_total_repos', result.totalReposInDb, now, 'metric');
            count++;
            insertMetric.run('reposignal_repos_imported', result.reposImported, now, 'metric');
            count++;
            // Record per-language counts
            for (const [lang, langCount] of Object.entries(result.languageBreakdown)) {
                if (langCount >= 10) {
                    insertMetric.run(`reposignal_lang_${lang.toLowerCase()}`, langCount, now, 'metric');
                    count++;
                }
            }
            // Record signal level counts
            for (const [level, levelCount] of Object.entries(result.signalBreakdown)) {
                insertMetric.run(`reposignal_${level}_count`, levelCount, now, 'metric');
                count++;
            }
        }
        catch (err) {
            this.log.debug('[reposignal] Error recording metrics:', err);
        }
        return count;
    }
    parseTopics(topics) {
        if (!topics)
            return [];
        try {
            return JSON.parse(topics);
        }
        catch {
            return [];
        }
    }
    getLastResult() {
        return this.lastResult;
    }
    /** Get import stats from state table. */
    getStats() {
        const total = this.brainDb.prepare('SELECT COUNT(*) as c FROM reposignal_import_state').get().c;
        const levels = this.brainDb.prepare('SELECT signal_level, COUNT(*) as c FROM reposignal_import_state GROUP BY signal_level').all();
        const last = this.brainDb.prepare('SELECT MAX(imported_at) as t FROM reposignal_import_state').get();
        const byLevel = {};
        for (const l of levels)
            byLevel[l.signal_level] = l.c;
        return { totalImported: total, byLevel, lastImport: last.t };
    }
}
//# sourceMappingURL=reposignal-importer.js.map