import axios from 'axios';
import { ChatMessage, ChatOptions, ProviderResponse } from '../../../types/chat';
import { StandardizedToolCall, Tool } from '../../../types/tool';
import { OpenRouterConfig } from './config';
import { OpenRouterChatRequestBody, OpenRouterChatResponse, OpenRouterContentPart, OpenRouterMessage } from './types';
import { convertToolSchema, convertMessages, extractToolCalls} from './tools';
import { getMaxTokens, supportsTools } from './modelConfig';

export async function chat(
  config: OpenRouterConfig,
  messages: ChatMessage[],
  options: ChatOptions,
  tools?: Tool[]
): Promise<ProviderResponse> {
  const url = config.getApiUrl('chat/completions');
  const model = config.model;
  const logger = config.getLogger();

  const requestBody: OpenRouterChatRequestBody = {
    messages: convertMessages(messages).map(msg => ({
      ...msg,
      content: convertContentToOpenRouterFormat(msg.content)
    })),
    model: model,
    max_tokens: options.maxTokens || getMaxTokens(model),
    temperature: options.temperature,
    stream: false
  };

  const modelSupportsTools = supportsTools(model);

  if (tools && tools.length > 0) {
    if (modelSupportsTools) {
      requestBody.tools = tools.map(tool => convertToolSchema(tool.getSchema()));
      requestBody.tool_choice = 'auto';
    } else {
      // Append instructions for using tools
      const toolInstructions = `
You have access to the following tools:
${tools.map(tool => `- ${tool.name}: ${tool.description}`).join('\n')}

To use a tool, output a function call in a code block like this:
\`\`\`function_call
{
  "name": "tool_name",
  "arguments": {
    "arg1": "value1",
    "arg2": "value2"
  }
}
\`\`\`
Replace "tool_name" with the actual tool name and provide the necessary arguments.

For example, to use the "tool_name" tool with arguments "arg1" and "arg2":

\`\`\`function_call
{
  "name": "tool_name",
  "arguments": {
    "arg1": "value1",
    "arg2": "value2"
  }
}
\`\`\
`;
      requestBody.messages[requestBody.messages.length - 1].content += '\n\n' + toolInstructions;
    }
  }

  const headers = {
    ...config.getHeaders(),
    'HTTP-Referer': config.httpReferer,
    'X-Title': config.xTitle
  };

  logger.debug('OpenRouter API request', { url, model, toolsCount: tools?.length });

  try {
    const response = await axios.post<OpenRouterChatResponse>(url, requestBody, { headers });
    logger.debug('OpenRouter API response received', { status: response.status });
    return createProviderResponse(response.data, modelSupportsTools, logger);
  } catch (error) {
    if (axios.isAxiosError(error)) {
      logger.error('OpenRouter API error', {
        status: error.response?.status,
        statusText: error.response?.statusText,
        data: error.response?.data
      });
    } else {
      logger.error('OpenRouter API error', { error });
    }
    throw error;
  }
}

function createProviderResponse(
  data: OpenRouterChatResponse, 
  modelSupportsTools: boolean,
  logger: ReturnType<OpenRouterConfig['getLogger']>
): ProviderResponse {
  const choice = data.choices[0];
  if (!choice) {
    throw new Error('Unexpected response format from OpenRouter API');
  }

  const message = choice.message || { role: 'assistant', content: '' };
  let content = message.content || '';
  let toolCalls: StandardizedToolCall[] = modelSupportsTools ? extractToolCalls(message as OpenRouterMessage) : [];

  if (!modelSupportsTools) {
    const functionCallRegex = /```function_call\n([\s\S]*?)```/g;
    let match;
    while ((match = functionCallRegex.exec(content)) !== null) {
      try {
        const functionCall = JSON.parse(match[1]);
        toolCalls.push({
          name: functionCall.name,
          arguments: functionCall.arguments
        });
      } catch (error) {
        logger.error('Error parsing function call', { error });
      }
    }
    content = content.replace(functionCallRegex, '');
  }

  return {
    content: content.trim(),
    toolCalls: toolCalls,
    usage: {
      promptTokens: data.usage?.prompt_tokens || 0,
      completionTokens: data.usage?.completion_tokens || 0,
      totalTokens: data.usage?.total_tokens || 0
    },
  };
}

function convertContentToOpenRouterFormat(content: string | OpenRouterContentPart[]): any {
  if (typeof content === 'string') {
    return content;
  }
  return content.map(part => ({
    type: part.type,
    content: (part as any)[part.type]
  }));
}
