import { ChatMessage, ChatOptions, ProviderResponse } from '../../types/chat';
import { ProviderCapabilities, IAIProvider, AIProviderMiddleware, AIProviderPlugin } from '../../types/provider';
import { Tool, GenericToolSchema, StandardizedToolCall } from '../../types/tool';
import { ILogger } from '../../types/common';

export abstract class BaseProvider implements IAIProvider {
  protected logger: ILogger;
  protected middleware: AIProviderMiddleware[] = [];
  protected plugins: AIProviderPlugin[] = [];

  constructor(logger: ILogger) {
    this.logger = logger;
  }

  abstract chat(messages: ChatMessage[], options: ChatOptions, tools?: Tool[]): Promise<ProviderResponse>;
  abstract streamChat(messages: ChatMessage[], options: ChatOptions, tools?: Tool[]): AsyncIterableIterator<ProviderResponse>;
  abstract getCapabilities(): ProviderCapabilities;
  abstract convertToolSchema(schema: GenericToolSchema): any;
  abstract convertToolCall(call: any): StandardizedToolCall;

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

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

  protected async applyMiddleware(messages: ChatMessage[]): Promise<ChatMessage[]> {
    let processedMessages = messages;
    for (const middleware of this.middleware) {
      processedMessages = await middleware.preProcess(processedMessages);
    }
    return processedMessages;
  }

  protected async applyMiddlewareToResponse(response: ProviderResponse): Promise<ProviderResponse> {
    let processedResponse = response;
    for (const middleware of this.middleware) {
      processedResponse = await middleware.postProcess(processedResponse);
    }
    return processedResponse;
  }

  protected async applyMiddlewareToToolCalls(toolCalls: StandardizedToolCall[]): Promise<StandardizedToolCall[]> {
    let processedToolCalls = toolCalls;
    for (const middleware of this.middleware) {
      if (middleware.processToolCalls) {
        processedToolCalls = await middleware.processToolCalls(processedToolCalls);
      }
    }
    return processedToolCalls;
  }
}