import { ChatMessage, ChatOptions, ProviderResponse } from '../../../types/chat';
import { Tool, GenericToolSchema, StandardizedToolCall } from '../../../types/tool';
import { IAIProvider, AIProviderMiddleware, AIProviderPlugin, ProviderCapabilities } from '../../../types/provider';
import { AnthropicProviderConfig } from '../../../types/provider';
import { AnthropicCacheKey } from './types';
import { AnthropicCache } from './cache';
import { chat } from './chat';
import { streamChat } from './streaming';
import { AnthropicConfig } from './config';
import { handleAnthropicError } from './errors';
import { ILogger } from '../../../types/common';
import { convertToolSchema, convertToolCall } from './tools';

export class AnthropicProvider implements IAIProvider {
  private config: AnthropicConfig;
  private cache: AnthropicCache;
  private middleware: AIProviderMiddleware[] = [];

  constructor(config: AnthropicProviderConfig, logger: ILogger) {
    this.config = new AnthropicConfig(config, logger);
    this.cache = new AnthropicCache();
  }

  async chat(messages: ChatMessage[], options: ChatOptions, tools?: Tool[]): Promise<ProviderResponse> {
    try {
      const cacheKey: AnthropicCacheKey = { messages, options, tools };
      if (this.cache.has(cacheKey)) {
        return this.cache.get(cacheKey)!;
      }

      let processedMessages = messages;
      for (const mw of this.middleware) {
        processedMessages = await mw.preProcess(processedMessages);
      }

      let response: ProviderResponse = await chat(this.config, processedMessages, options, tools);

      for (const mw of this.middleware) {
        response = await mw.postProcess(response);
      }

      if (response.toolCalls && response.toolCalls.length > 0) {
        for (const mw of this.middleware) {
          if (mw.processToolCalls) {
            response.toolCalls = await mw.processToolCalls(response.toolCalls);
          }
        }
      }

      this.cache.set(cacheKey, response);
      return response;
    } catch (error) {
      throw handleAnthropicError(error);
    }
  }

  async *streamChat(messages: ChatMessage[], options: ChatOptions, tools?: Tool[]): AsyncIterableIterator<ProviderResponse> {
    try {
      let processedMessages = messages;
      for (const mw of this.middleware) {
        processedMessages = await mw.preProcess(processedMessages);
      }

      const stream = streamChat(this.config, processedMessages, options, tools);

      for await (const chunk of stream) {
        let processedChunk = chunk;
        for (const mw of this.middleware) {
          processedChunk = await mw.postProcess(processedChunk);
        }

        if (processedChunk.toolCalls && processedChunk.toolCalls.length > 0) {
          for (const mw of this.middleware) {
            if (mw.processToolCalls) {
              processedChunk.toolCalls = await mw.processToolCalls(processedChunk.toolCalls);
            }
          }
        }

        yield processedChunk;
      }
    } catch (error) {
      throw handleAnthropicError(error);
    }
  }

  getCapabilities(): ProviderCapabilities {
    return {
      maxTokens: 100000, // Adjust based on Anthropic's actual limits
      supportsFunctionCalling: true,
      supportsStreaming: true,
      supportedModels: [this.config.model],
      maxSimultaneousCalls: 1, // Adjust based on Anthropic's rate limits
      supportsSemanticCaching: false,
    };
  }

  use(middleware: AIProviderMiddleware): void {
    this.middleware.push(middleware);
  }

  registerPlugin(plugin: AIProviderPlugin): void {
    plugin.initialize(this);
  }

  convertToolSchema(schema: GenericToolSchema): any {
    return convertToolSchema(schema);
  }

  convertToolCall(call: any): StandardizedToolCall {
    return convertToolCall(call);
  }

  clearCache(): void {
    this.cache.clear();
  }
}

export * from './types';
export { AnthropicProviderError } from './errors';