import Anthropic from '@anthropic-ai/sdk';
import { ChatMessage, ChatOptions, ProviderResponse } from '../../../types/chat';
import { Tool, ActionContext } from '../../../types/tool';
import { AnthropicConfig } from './config';
import { convertToolSchema, convertMessages, extractToolCalls } from './tools';
import { request } from 'http';

export async function* streamChat(
  config: AnthropicConfig,
  messages: ChatMessage[],
  options: ChatOptions,
  tools?: Tool[]
): AsyncIterableIterator<ProviderResponse> {
  const anthropic = new Anthropic({
    apiKey: config.apiKey,
  });

  const convertedMessages = convertMessages(messages);

  const requestBody: Anthropic.MessageCreateParams = {
    model: config.model,
    messages: convertedMessages as any,
    max_tokens: options.maxTokens || 1000,
    temperature: options.temperature || 0.7,
    stream: true,
    system: '',
  };

  if(convertedMessages.length > 0 && convertedMessages[0].role === 'system') {
    (requestBody as any).system = convertedMessages[0].content 
    requestBody.messages = requestBody.messages.slice(1);
  }

  if (tools && tools.length > 0) {
    requestBody.tools = tools.map(tool => convertToolSchema(tool.getSchema())) as Anthropic.Tool[];
  }

  console.log('Sending request to Anthropic API', { 
    model: requestBody.model, 
    messageCount: requestBody.messages.length,
    maxTokens: requestBody.max_tokens,
    temperature: requestBody.temperature,
    toolCount: tools?.length
  });

  try {
    const stream = await anthropic.messages.create(requestBody);

    console.log('Received response from Anthropic API');

    let aggregatedData = '';
    let currentToolCall: any = null;

    // Create a basic ActionContext
    const actionContext: ActionContext = {
      user: { id: 'default-user', name: 'Default User' },
      session: { id: 'default-session', timestamp: new Date().toISOString() },
    };

    for await (const chunk of stream) {
      console.log('Received chunk', { chunkSize: JSON.stringify(chunk).length });
      aggregatedData += JSON.stringify(chunk);
      try {
        const parsedData = JSON.parse(aggregatedData);
        console.log('Parsed data:', parsedData);

        // Note: In OpenAI's format, we would target choices[0].delta here.
        // Anthropic's API might structure the response differently, so we're using parsedData.delta directly.
        if (parsedData.delta && parsedData.delta.content) {
          const content = parsedData.delta.content;
          console.log('Content:', content);
          yield createProviderResponse({ content, toolCalls: [] });
        }

        if (parsedData.delta && parsedData.delta.tool_calls) {
          const toolCalls = parsedData.delta.tool_calls;
          console.log('Tool Calls:', toolCalls);

          for (const toolCall of toolCalls) {
            if (!currentToolCall || toolCall.index === 0) {
              currentToolCall = { ...toolCall, function: { ...toolCall.function } };
            } else {
              if (toolCall.function.name) {
                currentToolCall.function.name = toolCall.function.name;
              }
              if (toolCall.function.arguments) {
                currentToolCall.function.arguments = (currentToolCall.function.arguments || '') + toolCall.function.arguments;
              }
            }
          }

          console.log('Current Tool Call:', currentToolCall);

          if (parsedData.delta.type === 'tool_calls' && parsedData.delta.tool_calls[0].type === 'function') {
            try {
              const fullToolCall = {
                name: currentToolCall.function.name,
                arguments: JSON.parse(currentToolCall.function.arguments)
              };
              console.log('Full tool call:', fullToolCall);
              
              // Execute the tool
              const tool = tools?.find(t => t.name === fullToolCall.name);
              if (tool) {
                const result = await tool.execute(fullToolCall.arguments, actionContext);
                console.log('Tool execution result:', result);
                yield createProviderResponse({ content: `Tool ${fullToolCall.name} executed. Result: ${JSON.stringify(result)}`, toolCalls: [fullToolCall] });
              } else {
                console.error(`Tool ${fullToolCall.name} not found`);
                yield createProviderResponse({ content: `Error: Tool ${fullToolCall.name} not found`, toolCalls: [fullToolCall] });
              }
              
              currentToolCall = null;
            } catch (parseError) {
              console.error('Error parsing or executing tool call:', parseError);
              console.error('Raw tool call arguments:', currentToolCall.function.arguments);
              yield createProviderResponse({ content: `Error executing tool: ${parseError instanceof Error ? parseError.message : String(parseError)}`, toolCalls: [] });
            }
          }
        }

        aggregatedData = ''; // Reset aggregated data after successful parsing
      } catch (parseError) {
        // If parsing fails, continue aggregating data
        console.log('Parsing failed, continuing to aggregate data');
        continue;
      }
    }
  } catch (error) {
    handleError(config, error);
    throw error;
  }
}

function createProviderResponse(data: { content: string, toolCalls: any[] }): ProviderResponse {
  return {
    content: data.content,
    toolCalls: data.toolCalls,
    usage: { 
      promptTokens: 0,
      completionTokens: 0,
      totalTokens: 0
    },
  };
}

function handleError(config: AnthropicConfig, error: any): void {
  if (error instanceof Anthropic.APIError) {
    console.error('AnthropicProvider: API request failed:', {
      status: error.status,
      message: error.message,
    });
  } else {
    console.error('AnthropicProvider: Unexpected error:', error);
  }
}