/**
 * Briefing Service
 *
 * Generates a free-form text briefing memo by analyzing entities across
 * folders, personalized by the user's home brief and agent memory.
 * The memo is injected into the agent's system prompt as session context,
 * enabling the agent to greet users proactively with workspace findings.
 *
 * Replaces the structured JSON insight system.
 */
import { generateText } from 'ai';
import { resolveApiKey, resolveProvider, resolveModelId, loadUserConfig, createModel } from '../core/user-config.js';
import { log } from '../utils/log.js';
/** 7 days in ms — previous briefings older than this are discarded. */
const PREVIOUS_BRIEFING_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
/**
 * Gather entity summaries for briefing generation.
 * Includes temporal signals (due dates, staleness) for actionable analysis.
 */
function gatherEntityContext(wm, repoNames, maxEntities = 200) {
    const lines = [];
    let total = 0;
    const now = new Date();
    // Folder summaries first — gives the AI an accurate overview before entity details
    lines.push('=== FOLDER SUMMARIES ===');
    for (const repoName of repoNames) {
        try {
            const graph = wm.getGraph(repoName);
            const allEntities = graph.search({});
            const typeCounts = {};
            for (const e of allEntities) {
                const t = e.data?.entity_type ?? e.entityType ?? 'unknown';
                typeCounts[t] = (typeCounts[t] ?? 0) + 1;
            }
            const repoConfig = wm.getRepoConfig(repoName);
            const syncSources = repoConfig?.sync ?? [];
            const summary = Object.entries(typeCounts).map(([t, c]) => `${c} ${t}s`).join(', ');
            lines.push(`[${repoName}] ${allEntities.length} entities: ${summary}`);
            if (syncSources.length > 0) {
                lines.push(`  sync sources: ${syncSources.map((s) => s.source ?? 'unknown').join(', ')}`);
            }
        }
        catch { /* skip */ }
    }
    lines.push('');
    lines.push('=== ENTITY DETAILS ===');
    for (const repoName of repoNames) {
        try {
            const graph = wm.getGraph(repoName);
            const entities = graph.search({ sortBy: 'updated_at', limit: maxEntities });
            for (const entity of entities) {
                if (total >= maxEntities)
                    break;
                const fm = entity.data;
                const type = fm.entity_type ?? entity.entityType ?? 'unknown';
                const title = fm.name ?? entity.id;
                const wikilinks = entity.document?.wikilinks ?? [];
                const content = entity.document?.content ?? '';
                const snippet = content.length > 300 ? content.slice(0, 300) + '...' : content;
                lines.push(`[${repoName}/${type}/${entity.id}] ${title}`);
                // Temporal and status metadata
                const meta = [];
                if (fm.status)
                    meta.push(`status: ${fm.status}`);
                if (fm.priority)
                    meta.push(`priority: ${fm.priority}`);
                if (fm.stage)
                    meta.push(`stage: ${fm.stage}`);
                if (fm.assignee)
                    meta.push(`assignee: ${fm.assignee}`);
                if (fm.due_date) {
                    const dueDate = new Date(fm.due_date);
                    const daysUntil = Math.round((dueDate.getTime() - now.getTime()) / 86400000);
                    meta.push(`due: ${fm.due_date} (${daysUntil < 0 ? Math.abs(daysUntil) + 'd overdue' : daysUntil + 'd away'})`);
                }
                if (fm.close_date)
                    meta.push(`close_date: ${fm.close_date}`);
                if (fm.client)
                    meta.push(`client: ${fm.client}`);
                if (fm.project)
                    meta.push(`project: ${fm.project}`);
                if (fm.deal_value)
                    meta.push(`deal_value: ${fm.deal_value}`);
                if (fm.attendees)
                    meta.push(`attendees: ${JSON.stringify(fm.attendees)}`);
                if (fm.updated_at) {
                    const updated = new Date(fm.updated_at);
                    const daysSince = Math.round((now.getTime() - updated.getTime()) / 86400000);
                    if (daysSince > 14)
                        meta.push(`stale: ${daysSince}d since update`);
                }
                if (meta.length > 0)
                    lines.push(`  ${meta.join(' | ')}`);
                if (wikilinks.length > 0) {
                    lines.push(`  links: ${wikilinks.join(', ')}`);
                }
                if (snippet)
                    lines.push(`  content: ${snippet}`);
                lines.push('');
                total++;
            }
        }
        catch {
            // Skip inaccessible repos
        }
    }
    return { context: lines.join('\n'), entityCount: total };
}
function buildBriefingPrompt(userBrief, previousBriefing, agentMemory, tools) {
    const briefSection = userBrief
        ? `\nThe user has described what they care about most:\n"${userBrief}"\n\nPrioritize findings that align with their focus. But also surface critical issues they might not have asked about.`
        : '';
    const previousSection = previousBriefing
        ? `\n\nHere is the previous briefing. Focus on what CHANGED since then — new developments, resolved items, new risks. Do not repeat items that are unchanged:\n\n---\n${previousBriefing}\n---`
        : '';
    const memorySection = agentMemory
        ? `\n\nThe user's recent agent memory (includes dismissals — if the user said "don't worry about X", exclude that item from the briefing):\n\n${agentMemory}`
        : '';
    const toolList = tools.length > 0
        ? tools.map(t => `- ${t.name}: ${t.description}`).join('\n')
        : '(core workspace tools: create/update/search/delete entities, create views, lint, find orphans)';
    return `You are writing a daily briefing memo for a knowledge management workspace used by creative services teams. Analyze the entities provided and write a concise, actionable briefing.
${briefSection}

ACCURACY IS CRITICAL:
- The data includes folder summaries with exact entity type counts — trust these numbers
- Only make claims supported by the data shown
- Reference specific entities using [[wikilink]] format (e.g., [[meridian-rebrand]], [[jamie-torres]])

Write a natural briefing memo — NOT structured JSON. Organize by salience:
- Lead with the most important or urgent item
- Group related findings naturally
- Keep it under 500 words. Dense paragraphs, not exhaustive lists
- Be direct and specific: name entities, dates, amounts
- Frame findings as things the user should know or act on

What to surface:
- Overdue tasks, approaching deadlines, stalled projects
- Client engagement gaps, relationship signals
- Deal pipeline changes, opportunities
- Data quality issues (orphaned entities, missing links)
- Activity patterns and velocity changes

The user has an AI assistant with these tools — when suggesting actions, only suggest things the assistant can actually do:
${toolList}

CRITICAL: Only suggest actions that use the tools listed above. Do not reference external systems unless a connector tool is available.
${previousSection}${memorySection}

Write the briefing now. Plain text only, no JSON, no markdown headers, no bullet-point lists — flowing paragraphs.`;
}
/**
 * Generate a free-form briefing memo from workspace entities.
 */
export async function generateBriefing(wm, repoNames, userBrief, authService, userId, workspaceConfig, tools, agentMemory) {
    const { context, entityCount } = gatherEntityContext(wm, repoNames);
    if (entityCount === 0) {
        log.info('[briefing] No entities found, skipping generation');
        authService.setCachedBriefing(userId, '');
        return { briefingText: '', generatedAt: new Date().toISOString(), entityCount: 0 };
    }
    const userConfig = loadUserConfig();
    const apiKey = resolveApiKey(userConfig, workspaceConfig);
    const provider = resolveProvider(userConfig, workspaceConfig);
    const modelId = resolveModelId(userConfig, workspaceConfig);
    log.info(`[briefing] Provider: ${provider}, model: ${modelId}, apiKey: ${apiKey ? 'set' : 'MISSING'}, entities: ${entityCount}`);
    if (!apiKey) {
        log.warn('[briefing] No API key configured, returning empty');
        return { briefingText: '', generatedAt: new Date().toISOString(), entityCount };
    }
    const model = createModel(provider, modelId, apiKey);
    // Freshness guard: discard previous briefing if older than 7 days
    const cached = authService.getCachedBriefing(userId);
    let previousBriefing = cached.previousBriefingText;
    if (previousBriefing && cached.generatedAt) {
        const age = Date.now() - new Date(cached.generatedAt).getTime();
        if (age > PREVIOUS_BRIEFING_MAX_AGE_MS) {
            previousBriefing = null;
        }
    }
    // For Google thinking models (gemini-2.5-*), enable thinking with a budget
    const isGoogleThinking = provider === 'google' && modelId.includes('2.5');
    const providerOptions = isGoogleThinking
        ? { google: { thinkingConfig: { thinkingBudget: 1024 } } }
        : undefined;
    const response = await generateText({
        model,
        maxOutputTokens: 2000,
        system: buildBriefingPrompt(userBrief, previousBriefing, agentMemory ?? null, tools ?? []),
        messages: [
            {
                role: 'user',
                content: `Here are the entities in my workspace (today is ${new Date().toISOString().split('T')[0]}):\n\n${context}\n\nWrite my daily briefing.`,
            },
        ],
        providerOptions,
    });
    const briefingText = (response.text || '').trim();
    log.info(`[briefing] LLM finishReason: ${response.finishReason}, text: ${briefingText.length} chars, usage: ${JSON.stringify(response.usage)}`);
    authService.setCachedBriefing(userId, briefingText);
    return {
        briefingText,
        generatedAt: new Date().toISOString(),
        entityCount,
    };
}
//# sourceMappingURL=briefing.js.map