const OpenAI = require('openai');

const SUPPORTED_MODELS = new Set([
  'gpt-4o-mini',
  'gpt-4o',
  'gpt-4.1-mini',
  'gpt-4.1',
  'gpt-4'
]);
const DEFAULT_MODEL = 'gpt-4o-mini';
const DEFAULT_TEMPERATURE = 0.3;
const DEFAULT_MAX_OUTPUT_TOKENS = 800;

function resolveModel() {
  const requested = typeof process.env.OPENAI_MODEL === 'string'
    ? process.env.OPENAI_MODEL.trim()
    : '';

  if (!requested) {
    return DEFAULT_MODEL;
  }

  if (SUPPORTED_MODELS.has(requested)) {
    return requested;
  }

  console.warn(`aiService: unsupported OPENAI_MODEL value '${requested}'. Falling back to '${DEFAULT_MODEL}'.`);
  return DEFAULT_MODEL;
}

const ACTIVE_MODEL = resolveModel();

/**
 * Create and return an OpenAI client instance.
 * @returns {OpenAI}
 * @throws {Error} When the API key is missing.
 */
function createOpenAIClient() {
  const apiKey = process.env.OPENAI_API_KEY;
  if (!apiKey) {
    throw new Error('Missing OpenAI API key. Set process.env.OPENAI_API_KEY.');
  }

  return new OpenAI({ apiKey });
}

/**
 * Extract a plain assistant message from the OpenAI Responses API result.
 * @param {object} response
 * @returns {string}
 */
function extractAssistantResponse(response) {
  if (!response || typeof response !== 'object') {
    return '';
  }

  if (typeof response.output_text === 'string' && response.output_text.trim()) {
    return response.output_text.trim();
  }

  if (!Array.isArray(response.output)) {
    return '';
  }

  for (const item of response.output) {
    if (!item || item.type !== 'message') {
      continue;
    }

    const content = item.content;
    if (!Array.isArray(content)) {
      continue;
    }

    const textParts = content
      .filter((block) => block && typeof block.text === 'string')
      .map((block) => block.text.trim())
      .filter(Boolean);

    if (textParts.length > 0) {
      return textParts.join('\n').trim();
    }
  }

  return '';
}

/**
 * Format OpenAI client errors into useful error messages.
 * @param {any} error
 * @returns {Error}
 */
function formatOpenAIError(error) {
  const status = error?.response?.status;
  const bodyMessage = error?.response?.body?.error?.message || error?.message || 'Unknown error';

  if (status === 401 || status === 403) {
    return new Error(`OpenAI authentication failed. Check OPENAI_API_KEY. ${bodyMessage}`);
  }

  if (status === 429) {
    return new Error(`OpenAI rate limit exceeded. Please retry later. ${bodyMessage}`);
  }

  if (status) {
    return new Error(`OpenAI API error (${status}). ${bodyMessage}`);
  }

  return new Error(`OpenAI request failed. ${bodyMessage}`);
}

/**
 * Send a prompt to OpenAI and return the assistant response text.
 * @param {string} prompt
 * @returns {Promise<string>}
 */
async function generateAIResponse(prompt) {
  if (typeof prompt !== 'string' || !prompt.trim()) {
    throw new TypeError('prompt must be a non-empty string');
  }

  const client = createOpenAIClient();

  try {
    const selectedModel = process.env.OPENAI_MODEL || 'gpt-4o-mini';
    console.log('aiService: selected OpenAI model:', selectedModel);
    console.log('aiService: Calling OpenAI...');

    const response = await client.responses.create({
      model: selectedModel,
      input: prompt
    });

    console.log('aiService: OpenAI response received');
    console.log('aiService: full OpenAI response object:', JSON.stringify(response, null, 2));
    const assistantText = extractAssistantResponse(response);
    if (!assistantText) {
      throw new Error('OpenAI returned an empty assistant response.');
    }

    return assistantText;
  } catch (error) {
    console.error('aiService: OpenAI error.message:', error?.message);
    console.error('aiService: OpenAI error.status:', error?.status);
    console.error('aiService: OpenAI error.code:', error?.code);
    console.error('aiService: OpenAI error.response:', error?.response);
    console.error('aiService: OpenAI error.cause:', error?.cause);
    console.error('aiService: full error object:', error);
    throw error;
  }
}

module.exports = {
  generateAIResponse
};
