import { openai, defaultModel, serviceOptions, supportsWebSearch, parseWithRetry } from '../agent.js';
import type { InterpreterOutput, RetrievalCandidate, MediaType } from '../types/agents.js';
import { z } from 'zod';
import { zodTextFormat } from 'openai/helpers/zod';

const RetrievalBatchSchema = z.object({
  candidates: z.array(z.object({
    title: z.string(),
    type: z.enum(['movie', 'tv']),
    year: z.string(),
    reason: z.string()
  }))
});

export interface RetrievalBatchOutput {
  candidates: Array<{
    title: string;
    type: 'movie' | 'tv';
    year: string;
    reason: string;
  }>;
}

const RetrievalCandidateSchema = z.object({
  title: z.string(),
  reason: z.string()
});

export interface RetrievalOutput {
  candidates: RetrievalCandidate[];
}

function buildSystemPrompt(
  interpreterOutput: InterpreterOutput,
  mediaType: MediaType,
  useWebSearch: boolean,
  hardRequirements: boolean,
  seenTitles: string[]
): string {
  const mediaLabel = mediaType === 'movie' ? 'movie' : 'TV show';
  const mediaLabelPlural = mediaType === 'movie' ? 'movies' : 'TV shows';

  let prompt = `You are a ${mediaLabel} recommendation specialist. Your task is to recommend titles that match the user's taste profile.

USER TASTE PROFILE:
- Liked ${mediaLabelPlural}: ${interpreterOutput.liked.join(', ') || '(none)'}
- Disliked ${mediaLabelPlural}: ${interpreterOutput.disliked.join(', ') || '(none)'}
- Themes: ${interpreterOutput.themes.join(', ') || '(none)'}
- Tone: ${interpreterOutput.tone.join(', ') || '(none)'}
- Requirements: ${interpreterOutput.requirements.join(', ') || '(none)'}
- Avoid: ${interpreterOutput.avoid.join(', ') || '(none)'}

RESPONSE FORMAT:
Output exactly 10 recommendations in the following JSON format. No additional text.

[
  { "title": "Title Name", "type": "movie"|"tv", "year": "2024", "reason": "1-sentence why it matches" },
  ...
]

REQUIREMENTS:
- Output exactly 10 titles, no more, no less
- Each title must include: title, type (movie/tv), year, and a concise reason
- Prioritize variety across themes and decades
- Do not include titles the user already mentioned as liked/disliked
${seenTitles.length > 0 ? `- Do NOT repeat the following titles that have already been suggested: ${seenTitles.join(', ')}` : ''}
${useWebSearch ? '\n- Use web search to find recent and accurate titles, including newer releases' : ''}
${hardRequirements ? '\n- Strictly follow ALL requirements. Exclude any candidate that does not meet every stated requirement.' : ''}
- If asked for "more 10" or "more recommendations", provide 10 new recommendations following the same format`;

  return prompt;
}

export async function runRetrievalBatch(
  interpreterOutput: InterpreterOutput,
  mediaType: MediaType,
  useWebSearch: boolean,
  hardRequirements: boolean,
  seenTitles: string[],
  previousResponseId: string | null
): Promise<{ candidates: RetrievalCandidate[]; responseId: string }> {
  const canSearch = useWebSearch && supportsWebSearch;
  const systemPrompt = buildSystemPrompt(interpreterOutput, mediaType, useWebSearch, hardRequirements, seenTitles);

  let input: string;

  if (previousResponseId === null) {
    input = 'Please recommend 10 titles that match my taste profile. Output exactly 10 recommendations in the specified JSON format.';
  } else {
    input = 'Give me 10 more recommendations following the same JSON format as before.';
  }

  const response = await parseWithRetry(() => openai.responses.parse({
    model: defaultModel,
    ...serviceOptions,
    ...(canSearch ? { tools: [{ type: 'web_search' as const }] } : {}),
    ...(previousResponseId ? { previous_response_id: previousResponseId } : {}),
    text: { format: zodTextFormat(RetrievalBatchSchema, "candidates") },
    instructions: systemPrompt,
    input: input,
  }));

  const parsed = response.output_parsed as RetrievalBatchOutput | undefined;
  const responseId = response.id;

  if (!parsed || !parsed.candidates) {
    return { candidates: [], responseId };
  }

  const candidates: RetrievalCandidate[] = parsed.candidates.map((c) => ({
    title: c.title,
    reason: c.reason,
  }));

  return { candidates, responseId };
}