/**
 * LLM-based prompt selection for the gating flow.
 *
 * Sends tags + prompt index to the heavy LLM, which returns
 * a ranked list of relevant prompt names.
 */

import type { ProviderRegistry } from '../providers/registry.js';
import type { SystemPromptFetcher } from '../proxymodel/types.js';

export interface PromptIndexForLlm {
  name: string;
  priority: number;
  summary: string | null;
  chapters: string[] | null;
}

export interface LlmSelectionResult {
  selectedNames: string[];
  reasoning: string;
}

/**
 * Token budget for the selection call. A reasoning model spends most of its
 * budget inside reasoning_content before emitting the {selectedNames} JSON, so
 * 1024 was too tight (finish_reason=length, empty content → "bad JSON"). Give
 * it room for thinking + the small JSON answer. Override via env.
 */
const SELECT_MAX_TOKENS = (() => {
  const v = Number(process.env['MCPCTL_GATE_SELECT_MAX_TOKENS']);
  return Number.isFinite(v) && v > 0 ? v : 4000;
})();

export class LlmPromptSelector {
  constructor(
    private readonly providerRegistry: ProviderRegistry,
  ) {}

  async selectPrompts(
    tags: string[],
    promptIndex: PromptIndexForLlm[],
    getSystemPromptFn?: SystemPromptFetcher,
    signal?: AbortSignal,
  ): Promise<LlmSelectionResult> {
    const DEFAULT_SYSTEM_PROMPT = `You are a context selection assistant. Given a developer's task keywords and a list of available project prompts, select which prompts are relevant to their work. Return a JSON object with "selectedNames" (array of prompt names) and "reasoning" (brief explanation). Priority 10 prompts must always be included.`;
    const systemPrompt = getSystemPromptFn
      ? await getSystemPromptFn('llm-gate-context-selector', DEFAULT_SYSTEM_PROMPT)
      : DEFAULT_SYSTEM_PROMPT;

    const userPrompt = `Task keywords: ${tags.join(', ')}

Available prompts:
${promptIndex.map((p) => `- ${p.name} (priority: ${p.priority}): ${p.summary ?? 'No summary'}${p.chapters?.length ? `\n  Chapters: ${p.chapters.join(', ')}` : ''}`).join('\n')}

Select the relevant prompts. Return JSON: { "selectedNames": [...], "reasoning": "..." }`;

    const provider = this.providerRegistry.getProvider('heavy');
    if (!provider) {
      throw new Error('No heavy LLM provider available');
    }

    // Deliberately NOT setting `model` here. Prompt-ranking only needs a
    // working LLM; forcing the project's vLLM model onto the (anthropic) heavy
    // provider made every selection fail silently. Use the provider's own model.
    const completionOptions: import('../providers/types.js').CompletionOptions = {
      messages: [
        { role: 'system', content: systemPrompt },
        { role: 'user', content: userPrompt },
      ],
      temperature: 0,
      maxTokens: SELECT_MAX_TOKENS,
      ...(signal ? { signal } : {}),
    };

    const result = await provider.complete(completionOptions);

    const response = result.content;

    // Parse JSON from response (may be wrapped in markdown code blocks)
    const jsonMatch = response.match(/\{[\s\S]*"selectedNames"[\s\S]*\}/);
    if (!jsonMatch) {
      throw new Error('LLM response did not contain valid selection JSON');
    }

    const parsed = JSON.parse(jsonMatch[0]) as { selectedNames?: string[]; reasoning?: string };
    const selectedNames = parsed.selectedNames ?? [];
    const reasoning = parsed.reasoning ?? '';

    // Always include priority 10 prompts
    for (const p of promptIndex) {
      if (p.priority === 10 && !selectedNames.includes(p.name)) {
        selectedNames.push(p.name);
      }
    }

    return { selectedNames, reasoning };
  }
}
