/**
 * 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;
}

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

  async selectPrompts(
    tags: string[],
    promptIndex: PromptIndexForLlm[],
    getSystemPromptFn?: SystemPromptFetcher,
  ): 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');
    }

    const completionOptions: import('../providers/types.js').CompletionOptions = {
      messages: [
        { role: 'system', content: systemPrompt },
        { role: 'user', content: userPrompt },
      ],
      temperature: 0,
      maxTokens: 1024,
    };
    if (this.modelOverride) {
      completionOptions.model = this.modelOverride;
    }

    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 };
  }
}
