import * as fs from 'node:fs';
import * as path from 'node:path';
import { createEmbedder, LanceStore, requireEmbedding, TotemConfigSchema } from '@mmnto/totem';
import { loadEnv } from '../utils.js';
// ─── Branch Parsing ───────────────────────────────────────
const TICKET_RE = /(\d+)/;
const DEFAULT_BRANCHES = new Set(['main', 'master', 'develop', 'dev']);
export function parseBranch(branchRef) {
    const branch = branchRef.trim();
    if (!branch || DEFAULT_BRANCHES.has(branch)) {
        return { ticket: null, query: 'project overview' };
    }
    const ticketMatch = branch.match(TICKET_RE);
    const ticket = ticketMatch ? ticketMatch[1] : null;
    // Build a human-readable query from the branch name
    // e.g. "feat/1095-session-start-v2" → "1095 session start v2"
    const query = branch
        .replace(/^[a-z]+\//i, '') // strip prefix (feat/, fix/, chore/)
        .replace(/[-_/]/g, ' ') // delimiters to spaces
        .trim();
    return { ticket, query: query || 'project overview' };
}
// ─── Budget Truncation ────────────────────────────────────
const DEFAULT_MAX_CHARS = 10_000;
const DEFAULT_LIMIT = 5;
function formatResult(result, index) {
    const header = `### ${index + 1}. ${result.label} (${result.type})`;
    const meta = `**File:** ${result.filePath} | **Score:** ${result.score.toFixed(3)}`;
    const body = result.content;
    return `${header}\n${meta}\n\n${body}`;
}
export function truncateResults(results, maxCharacters) {
    if (results.length === 0)
        return { content: '', included: 0 };
    const blocks = [];
    let totalChars = 0;
    let included = 0;
    for (let i = 0; i < results.length; i++) {
        const block = formatResult(results[i], i);
        if (totalChars + block.length > maxCharacters && included > 0)
            break;
        blocks.push(block);
        totalChars += block.length;
        included++;
    }
    const omitted = results.length - included;
    let content = blocks.join('\n\n---\n\n');
    if (omitted > 0) {
        content += `\n\n(${omitted} additional result${omitted > 1 ? 's' : ''} omitted for token budget)`;
    }
    return { content, included };
}
// ─── Config Loading (lightweight) ─────────────────────────
async function loadConfig(configPath) {
    const { createJiti } = await import('jiti');
    const jiti = createJiti(import.meta.url);
    const mod = (await jiti.import(configPath));
    const raw = mod['default'] ?? mod;
    return TotemConfigSchema.parse(raw);
}
// ─── Main Pipeline ────────────────────────────────────────
export async function getAutoContext(options) {
    const start = Date.now();
    const projectRoot = options?.projectRoot ?? process.cwd();
    const maxCharacters = options?.maxCharacters ?? DEFAULT_MAX_CHARS;
    const limit = options?.limit ?? DEFAULT_LIMIT;
    const branchRef = options?.branchRef ?? 'main';
    const { query } = parseBranch(branchRef);
    const empty = {
        query,
        resultsIncluded: 0,
        totalFound: 0,
        content: '',
        searchMethod: 'none',
        durationMs: Date.now() - start,
    };
    // ── Locate config & LanceDB ──
    const configPath = path.join(projectRoot, 'totem.config.ts');
    if (!fs.existsSync(configPath))
        return { ...empty, durationMs: Date.now() - start };
    let config;
    try {
        loadEnv(projectRoot);
        config = await loadConfig(configPath);
    }
    catch {
        return { ...empty, durationMs: Date.now() - start };
    }
    const storePath = path.join(projectRoot, config.lanceDir);
    if (!fs.existsSync(storePath))
        return { ...empty, durationMs: Date.now() - start };
    // ── Try vector/hybrid search first ──
    let store = null;
    let results = [];
    let searchMethod = 'none';
    try {
        const embedding = requireEmbedding(config);
        const embedder = createEmbedder(embedding);
        store = new LanceStore(storePath, embedder, { absolutePathRoot: projectRoot }, (msg) => {
            process.stderr.write(`[auto-context] ${msg}\n`);
        });
        await store.connect();
        if (await store.isEmpty())
            return { ...empty, durationMs: Date.now() - start };
        results = await store.search({ query, maxResults: limit });
        searchMethod = 'hybrid';
    }
    catch (err) {
        // Embedder failed (no API key, network error, etc.) — try FTS fallback
        const detail = err instanceof Error ? (err.stack ?? err.message) : String(err);
        process.stderr.write(`[auto-context] Vector search failed, trying FTS fallback: ${detail}\n`);
        try {
            if (!store) {
                // Create store with a stub embedder — only FTS will be used (no embed calls).
                const stubEmbedder = {
                    dimensions: 0,
                    embed: (_texts) => Promise.reject(new Error('[Totem Error] No embedder available for FTS fallback')),
                };
                store = new LanceStore(storePath, stubEmbedder, { absolutePathRoot: projectRoot }, (msg) => {
                    process.stderr.write(`[auto-context] ${msg}\n`);
                });
            }
            // (Re-)connect with connectFtsOnly — skips dimension validation and
            // never nukes the index. Recovers from failed connect() state too.
            await store.connectFtsOnly();
            if (await store.isEmpty())
                return { ...empty, durationMs: Date.now() - start };
            results = await store.searchFts({ query, maxResults: limit });
            searchMethod = 'fts';
        }
        catch (ftsErr) {
            const ftsMsg = ftsErr instanceof Error ? ftsErr.message : String(ftsErr);
            process.stderr.write(`[auto-context] FTS fallback also failed: ${ftsMsg}\n`);
            return { ...empty, durationMs: Date.now() - start };
        }
    }
    const { content, included } = truncateResults(results, maxCharacters);
    return {
        query,
        resultsIncluded: included,
        totalFound: results.length,
        content,
        searchMethod,
        durationMs: Date.now() - start,
    };
}
//# sourceMappingURL=auto-context.js.map