import { LLMProvider } from './LLMProvider';
import type { StreamChunk, LLMResponse } from './StreamingTypes';
import type { Message } from '../core/AgentState';
import { ConfigManager } from '../../core/config/ConfigManager';

/**
 * Anthropic Claude API provider implementation
 */
export class ClaudeProvider extends LLMProvider {
  readonly name = 'Claude';
  
  private apiKey: string;
  private model: string;
  private baseURL: string = 'https://api.anthropic.com';
  private apiEndpoint: string;
  
  constructor() {
    super();
    
    const configManager = ConfigManager.instance();
    this.apiKey = configManager.get('general.claude.api_key') as string;
    this.model = configManager.get('general.claude.model') as string;
    this.apiEndpoint = `${this.baseURL}/v1/messages`;
  }

  /**
   * Convert internal messages to Claude's format
   */
  private convertMessages(messages: Message[], systemPrompt?: string): {
    system?: string;
    messages: Array<{ role: 'user' | 'assistant'; content: string }>;
  } {
    const claudeMessages: Array<{ role: 'user' | 'assistant'; content: string }> = [];
    
    claudeMessages.push(...messages.map(msg => ({ role: msg.role, content: msg.content })));
    
    return {
      system: systemPrompt,
      messages: claudeMessages
    };
  }

  /**
   * Parse Claude's streaming response chunks
   */
  private parseClaudeStreamChunk(line: string): { content?: string; isDone?: boolean } {
    if (!line.startsWith('data: ')) {
      return {};
    }

    const data = line.slice(6);
    if (data === '[DONE]') {
      return { isDone: true };
    }

    try {
      const json = JSON.parse(data);
      
      // Handle different Claude streaming event types
      switch (json.type) {
        case 'content_block_delta':
          return {
            content: json.delta?.text,
            isDone: false
          };
        case 'message_stop':
          return { isDone: true };
        default:
          return {};
      }
    } catch {
      return {}; // Skip invalid JSON lines
    }
  }
  
  async *generateStream(
    messages: Message[], 
    systemPrompt?: string,
    tools?: Record<string, unknown>[]
  ): AsyncIterableIterator<StreamChunk> {
    const { system, messages: claudeMessages } = this.convertMessages(messages, systemPrompt);
    
    const requestBody: {
      model: string;
      max_tokens: number;
      messages: Array<{ role: 'user' | 'assistant'; content: string }>;
      stream: boolean;
      system?: string;
      tools?: Record<string, unknown>[];
    } = {
      model: this.model,
      max_tokens: 8192,
      messages: claudeMessages,
      stream: true
    };
    
    if (system) {
      requestBody.system = system;
    }
    
    if (tools && tools.length > 0) {
      requestBody.tools = tools;
    }

    const response = await fetch(this.apiEndpoint, {
      method: 'POST',
      headers: {
        'x-api-key': this.apiKey,
        'Content-Type': 'application/json',
        'anthropic-version': '2023-06-01'
      },
      body: JSON.stringify(requestBody),
      mode: 'cors'
    });
    
    if (!response.ok) {
      throw new Error(`Claude API error: ${response.status} ${response.statusText}`);
    }
    
    const reader = response.body?.getReader();
    if (!reader) {
      throw new Error('Failed to get response reader');
    }
    
    const decoder = new TextDecoder();
    let buffer = '';
    
    try {
      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        
        buffer += decoder.decode(value, { stream: true });
        
        const lines = buffer.split('\n');
        buffer = lines.pop() || '';
        
        for (const line of lines) {
          const trimmedLine = line.trim();
          if (!trimmedLine) continue;
          
          const parseResult = this.parseClaudeStreamChunk(trimmedLine);
          
          if (parseResult.isDone) {
            yield { type: 'done', content: '' };
            return;
          }
          
          if (parseResult.content) {
            yield {
              type: 'text',
              content: parseResult.content
            };
          }
        }
      }
    } finally {
      reader.releaseLock();
    }
  }
  
  async generateCompletion(
    messages: Message[], 
    systemPrompt?: string,
    tools?: Record<string, unknown>[]
  ): Promise<LLMResponse> {
    const { system, messages: claudeMessages } = this.convertMessages(messages, systemPrompt);
    
    const requestBody: {
      model: string;
      max_tokens: number;
      messages: Array<{ role: 'user' | 'assistant'; content: string }>;
      stream: boolean;
      system?: string;
      tools?: Record<string, unknown>[];
    } = {
      model: this.model,
      max_tokens: 8192,
      messages: claudeMessages,
      stream: false
    };
    
    if (system) {
      requestBody.system = system;
    }
    
    if (tools && tools.length > 0) {
      requestBody.tools = tools;
    }

    const response = await fetch(this.apiEndpoint, {
      method: 'POST',
      headers: {
        'x-api-key': this.apiKey,
        'Content-Type': 'application/json',
        'anthropic-version': '2023-06-01'
      },
      body: JSON.stringify(requestBody),
      mode: 'cors'
    });
    
    if (!response.ok) {
      throw new Error(`Claude API error: ${response.status} ${response.statusText}`);
    }
    
    const data = await response.json();
    
    // Extract content from Claude's response format
    const content = data.content
      ?.filter((block: { type: string }) => block.type === 'text')
      ?.map((block: { text: string }) => block.text)
      ?.join('') || '';
    
    // Extract tool calls if present
    const toolCalls = data.content
      ?.filter((block: { type: string }) => block.type === 'tool_use')
      ?.map((block: { id: string; name: string; input: Record<string, unknown> }) => ({
        id: block.id,
        name: block.name,
        parameters: block.input
      })) || [];
    
    return {
      content,
      toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
      finished: data.stop_reason === 'end_turn' || data.stop_reason === 'tool_use'
    };
  }
}