export interface ChatRequest {
  message: string;
  sessionId?: string;
  context?: Record<string, any>;
}

export interface ChatResponse {
  id: string;
  content: string;
  timestamp: Date;
  sessionId: string;
  sources?: Array<{
    title: string;
    content: string;
    url?: string;
    score: number;
  }>;
  metadata?: Record<string, any>;
}

export interface StreamingChatResponse {
  id: string;
  content: string;
  isComplete: boolean;
  timestamp: Date;
  sessionId: string;
  sources?: Array<{
    title: string;
    content: string;
    url?: string;
    score: number;
  }>;
  error?: string;
}

export class ChatbotAPIError extends Error {
  constructor(message: string, public status?: number, public code?: string) {
    super(message);
    this.name = "ChatbotAPIError";
  }
}

export class ChatbotAPI {
  private baseUrl: string;
  private apiKey?: string;
  private sessionId: string;

  constructor(baseUrl: string = "/api/chatbot", apiKey?: string) {
    this.baseUrl = baseUrl;
    this.apiKey = apiKey;
    this.sessionId = this.generateSessionId();
  }

  private generateSessionId(): string {
    return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }

  private async makeRequest<T>(
    endpoint: string,
    options: RequestInit = {}
  ): Promise<T> {
    const url = `${this.baseUrl}${endpoint}`;
    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      ...(options.headers as Record<string, string>),
    };

    if (this.apiKey) {
      headers["Authorization"] = `Bearer ${this.apiKey}`;
    }

    try {
      const response = await fetch(url, {
        ...options,
        headers,
      });

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        throw new ChatbotAPIError(
          errorData.message ||
            `HTTP ${response.status}: ${response.statusText}`,
          response.status,
          errorData.code
        );
      }

      return await response.json();
    } catch (error) {
      if (error instanceof ChatbotAPIError) {
        throw error;
      }

      if (error instanceof TypeError && error.message.includes("fetch")) {
        throw new ChatbotAPIError(
          "네트워크 연결을 확인해주세요",
          0,
          "NETWORK_ERROR"
        );
      }

      throw new ChatbotAPIError(
        "API 요청 중 오류가 발생했습니다",
        0,
        "UNKNOWN_ERROR"
      );
    }
  }

  async sendMessage(request: ChatRequest): Promise<ChatResponse> {
    const response = await this.makeRequest<ChatResponse>("/chat", {
      method: "POST",
      body: JSON.stringify({
        ...request,
        sessionId: request.sessionId || this.sessionId,
      }),
    });

    return {
      ...response,
      timestamp: new Date(response.timestamp),
    };
  }

  async *streamMessage(
    request: ChatRequest
  ): AsyncGenerator<StreamingChatResponse, void, unknown> {
    const url = `${this.baseUrl}/chat/stream`;
    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      Accept: "text/event-stream",
    };

    if (this.apiKey) {
      headers["Authorization"] = `Bearer ${this.apiKey}`;
    }

    try {
      const response = await fetch(url, {
        method: "POST",
        headers,
        body: JSON.stringify({
          ...request,
          sessionId: request.sessionId || this.sessionId,
        }),
      });

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        throw new ChatbotAPIError(
          errorData.message ||
            `HTTP ${response.status}: ${response.statusText}`,
          response.status,
          errorData.code
        );
      }

      const reader = response.body?.getReader();
      if (!reader) {
        throw new ChatbotAPIError(
          "스트림을 읽을 수 없습니다",
          0,
          "STREAM_ERROR"
        );
      }

      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) {
            if (line.startsWith("data: ")) {
              const data = line.slice(6);

              if (data === "[DONE]") {
                return;
              }

              try {
                const parsed: StreamingChatResponse = JSON.parse(data);
                yield {
                  ...parsed,
                  timestamp: new Date(parsed.timestamp),
                };
              } catch (parseError) {
                console.warn("Failed to parse streaming response:", parseError);
              }
            }
          }
        }
      } finally {
        reader.releaseLock();
      }
    } catch (error) {
      if (error instanceof ChatbotAPIError) {
        throw error;
      }

      throw new ChatbotAPIError(
        "스트리밍 중 오류가 발생했습니다",
        0,
        "STREAM_ERROR"
      );
    }
  }

  async getSessionHistory(sessionId?: string): Promise<ChatResponse[]> {
    const id = sessionId || this.sessionId;
    const response = await this.makeRequest<{ messages: ChatResponse[] }>(
      `/sessions/${id}/history`
    );

    return response.messages.map((msg) => ({
      ...msg,
      timestamp: new Date(msg.timestamp),
    }));
  }

  async clearSession(sessionId?: string): Promise<void> {
    const id = sessionId || this.sessionId;
    await this.makeRequest(`/sessions/${id}`, {
      method: "DELETE",
    });
  }

  getSessionId(): string {
    return this.sessionId;
  }

  setSessionId(sessionId: string): void {
    this.sessionId = sessionId;
  }

  // Health check for the API
  async healthCheck(): Promise<{ status: string; timestamp: Date }> {
    const response = await this.makeRequest<{
      status: string;
      timestamp: string;
    }>("/health");
    return {
      ...response,
      timestamp: new Date(response.timestamp),
    };
  }
}

// Hook for using the chatbot API
export function useChatbotAPI(baseUrl?: string, apiKey?: string) {
  const api = new ChatbotAPI(baseUrl, apiKey);
  return api;
}

// Retry mechanism for failed requests
export async function withRetry<T>(
  operation: () => Promise<T>,
  maxRetries: number = 3,
  delay: number = 1000
): Promise<T> {
  let lastError: Error;

  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await operation();
    } catch (error) {
      lastError = error as Error;

      // Don't retry on client errors (4xx)
      if (
        error instanceof ChatbotAPIError &&
        error.status &&
        error.status >= 400 &&
        error.status < 500
      ) {
        throw error;
      }

      if (attempt < maxRetries) {
        await new Promise((resolve) => setTimeout(resolve, delay * attempt));
      }
    }
  }

  throw lastError!;
}
