import { getLlmClientConfig } from '@server/services/ai-providers.service';

export interface TranscriptEntry {
  role: 'system' | 'user' | 'assistant';
  text: string;
  ts: string;
}

export interface LlmResponse {
  text: string;
  shouldEscalate: boolean;
}

const RETRY_DELAY_MS = 1200;
const PLEASE_REPEAT = 'I am sorry, I did not quite catch that. Could you please repeat what you said?';
const NO_MODEL_MSG = 'No language model is configured for this agent. Please contact the restaurant directly or ask to speak with a staff member.';
const NO_KEY_MSG = 'The AI service is currently unavailable. Please try again in a moment or ask to speak with a staff member.';

async function callLlmApi(
  config: { apiBaseUrl: string; authHeaderName: string; authHeaderValue: string | null; modelId: string },
  messages: { role: string; content: string }[]
): Promise<Response> {
  return fetch(`${config.apiBaseUrl}/chat/completions`, {
    method: 'POST',
    headers: {
      [config.authHeaderName]: config.authHeaderValue || '',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model: config.modelId,
      messages,
      max_tokens: 300,
      temperature: 0.7,
    }),
  });
}

export async function generateResponse(
  llmModelId: string | null,
  systemPrompt: string,
  transcript: TranscriptEntry[],
  callerInput: string,
  restaurantId?: string | null
): Promise<LlmResponse> {
  if (!llmModelId) {
    return { text: NO_MODEL_MSG, shouldEscalate: true };
  }

  const config = await getLlmClientConfig(llmModelId, restaurantId);
  if (!config.apiKey) {
    return { text: NO_KEY_MSG, shouldEscalate: false };
  }

  const messages = buildMessages(systemPrompt, transcript, callerInput);

  let res: Response;
  try {
    res = await callLlmApi(config, messages);
  } catch (networkErr: unknown) {
    const msg = networkErr instanceof Error ? networkErr.message : String(networkErr);
    console.error(`[LLM] Network error on first attempt (${config.modelId}): ${msg}`);
    await new Promise(resolve => setTimeout(resolve, RETRY_DELAY_MS));
    try {
      res = await callLlmApi(config, messages);
    } catch (retryErr: unknown) {
      const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);
      console.error(`[LLM] Network error on retry (${config.modelId}): ${retryMsg}`);
      return { text: PLEASE_REPEAT, shouldEscalate: false };
    }
  }

  if (!res.ok) {
    const errText = await res.text();
    console.error(`[LLM] API error on first attempt (${res.status}) model=${config.modelId}:`, errText);

    if (res.status === 401 || res.status === 403) {
      return { text: NO_KEY_MSG, shouldEscalate: false };
    }

    if (res.status >= 500 || res.status === 429) {
      await new Promise(resolve => setTimeout(resolve, RETRY_DELAY_MS));
      let retryRes: Response;
      try {
        retryRes = await callLlmApi(config, messages);
      } catch (retryNetErr: unknown) {
        const msg = retryNetErr instanceof Error ? retryNetErr.message : String(retryNetErr);
        console.error(`[LLM] Network error on retry after ${res.status} (${config.modelId}): ${msg}`);
        return { text: PLEASE_REPEAT, shouldEscalate: false };
      }

      if (!retryRes.ok) {
        const retryErrText = await retryRes.text();
        console.error(`[LLM] API error on retry (${retryRes.status}) model=${config.modelId}:`, retryErrText);
        return { text: PLEASE_REPEAT, shouldEscalate: false };
      }

      return parseResponse(await retryRes.json() as { choices?: { message?: { content?: string } }[] });
    }

    return { text: PLEASE_REPEAT, shouldEscalate: false };
  }

  return parseResponse(await res.json() as { choices?: { message?: { content?: string } }[] });
}

function parseResponse(data: { choices?: { message?: { content?: string } }[] }): LlmResponse {
  const responseText = data.choices?.[0]?.message?.content?.trim() || '';
  if (!responseText) {
    return { text: PLEASE_REPEAT, shouldEscalate: false };
  }
  const shouldEscalate = /\b(transfer|connect you|human agent|staff member|escalat)/i.test(responseText);
  return { text: responseText, shouldEscalate };
}

function buildMessages(
  systemPrompt: string,
  transcript: TranscriptEntry[],
  callerInput: string
): { role: string; content: string }[] {
  const msgs: { role: string; content: string }[] = [
    { role: 'system', content: systemPrompt },
  ];

  for (const entry of transcript) {
    if (entry.role === 'system') continue;
    msgs.push({
      role: entry.role === 'user' ? 'user' : 'assistant',
      content: entry.text,
    });
  }

  msgs.push({ role: 'user', content: callerInput });
  return msgs;
}
