import { IChatLog } from '@/lib/db/schema';
import {
  ConnectionValidationResult,
  ApiErrorType,
  createValidationSuccess,
  createValidationError,
} from '@/types/settings';

interface LLMRequest {
  apiKey: string;
  baseUrl: string;
  model: string;
  messages: { role: string; content: string }[];
}

/**
 * Error response from API providers
 */
interface ApiErrorResponse {
  error?: {
    code?: string;
    message?: string;
    type?: string;
  };
}

export class LLMService {
  /**
   * Validate connection to LLM provider with detailed error information
   * @param baseUrl - The API base URL
   * @param apiKey - The API key for authentication
   * @param model - The model name to test
   * @returns Promise resolving to ConnectionValidationResult with detailed error info
   */
  /**
   * Validate connection to LLM provider with detailed error information
   * @param baseUrl - The API base URL
   * @param apiKey - The API key for authentication
   * @param model - The model name to test
   * @returns Promise resolving to ConnectionValidationResult with detailed error info
   */
  static async validateConnection(
    baseUrl: string,
    apiKey: string,
    model: string
  ): Promise<ConnectionValidationResult> {
    try {
      // Use our own server-side proxy to avoid CORS and Mixed Content issues
      const response = await fetch('/api/llm', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          apiKey,
          baseUrl,
          model,
          messages: [{ role: 'user', content: 'hello' }],
          stream: false, // Don't stream for validation
          // We don't need max_tokens as we just want to see if it works
        }),
      });

      const data = await response.json();

      if (response.ok && data.success) {
        return createValidationSuccess();
      }

      // Handle proxy errors or upstream errors returned by proxy
      return this.parseApiError(response, data);
    } catch (error) {
      // Handle network errors, timeouts, etc.
      if (this.isNetworkError(error)) {
        return createValidationError(ApiErrorType.NETWORK_ERROR, error);
      }
      // Unknown error
      return createValidationError(ApiErrorType.UNKNOWN, error);
    }
  }

  /**
   * Parse API error response to determine error type and message
   * @param response - The fetch response object
   * @param body - The parsed response body (if available)
   * @returns ConnectionValidationResult with appropriate error type
   */
  private static parseApiError(
    response: Response,
    body: unknown
  ): ConnectionValidationResult {
    const status = response.status;
    const errorData = body as ApiErrorResponse;
    // Proxy returns structured error in errorData.error
    const errorCode = errorData?.error?.code?.toLowerCase() || '';
    const errorMessage = errorData?.error?.message || '';

    // 401 Unauthorized / 403 Forbidden -> Invalid API Key
    if (status === 401 || status === 403 || errorCode === 'invalid_api_key') {
      return createValidationError(ApiErrorType.INVALID_KEY, errorData);
    }

    // 404 Not Found -> Could be model or URL
    if (status === 404 || errorCode === 'model_not_found') {
      if (errorCode.includes('model') || errorMessage.toLowerCase().includes('model')) {
        return createValidationError(ApiErrorType.MODEL_NOT_FOUND, errorData);
      }
      return createValidationError(ApiErrorType.INVALID_URL, errorData);
    }

    // 429 Too Many Requests -> Quota exceeded
    if (status === 429 || errorCode === 'rate_limit') {
      return createValidationError(ApiErrorType.QUOTA_EXCEEDED, errorData);
    }

    // Other errors -> Unknown
    return createValidationError(ApiErrorType.UNKNOWN, errorData);
  }

  /**
   * Check if an error is a network/timeout error
   * @param error - The error to check
   * @returns true if this is a network error
   */
  private static isNetworkError(error: unknown): boolean {
    if (error instanceof TypeError) {
      // TypeError often indicates network failure (e.g., 'Failed to fetch')
      return error.message.includes('fetch') || error.message.includes('network');
    }
    if (error instanceof Error && (error as any).name === 'AbortError') {
      // AbortError indicates timeout
      return true;
    }
    return false;
  }

  static async generateResponse(request: LLMRequest): Promise<string> {
    const { apiKey, baseUrl, model, messages } = request;

    try {
      // Use our own server-side proxy
      const response = await fetch('/api/llm', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          apiKey,
          baseUrl,
          model,
          messages: messages.map(m => ({ role: m.role, content: m.content })),
          temperature: 0.7,
          stream: false // Non-streaming for this method
        })
      });

      const data = await response.json();

      if (!response.ok || !data.success) {
        throw new Error(data.error?.message || `API Error: ${response.statusText}`);
      }

      return data.data?.text || '';
    } catch (error) {
      console.error('LLM Generation failed:', error);
      throw error;
    }
  }

  // --- Agent Logic ---

  static async getTeacherResponseStream(
    content: string,
    history: { role: string; content: string }[],
    callbacks: {
      onIntent?: (intent: import('@/lib/llm/intent-detector').Intent) => void;
      onToken: (token: string) => void;
      onComplete: (fullText: string) => void;
      onError: (error: any) => void;
    }
  ): Promise<void> {
    try {
      const { ProviderManagementService } = await import('./provider-management-service');
      const settings = ProviderManagementService.getActiveProviderSettings();
      const { TEACHER_AGENT_PROMPT } = await import('@/lib/agents/teacher');

      if (!settings.apiKey) throw new Error("AI Provider not configured");

      const systemMessage = { role: 'system', content: TEACHER_AGENT_PROMPT };
      const messages = [systemMessage, ...history, { role: 'user', content }];

      // For MVP, we are not actually streaming yet because the proxy doesn't support it well 
      // without more complex setup. We will simulate streaming for the UI feel.
      const response = await this.generateResponse({
        apiKey: settings.apiKey,
        baseUrl: settings.baseUrl,
        model: settings.modelName,
        messages: messages
      });

      // Simulation of streaming
      const tokens = response.split(' ');
      let currentText = '';

      for (const token of tokens) {
        currentText += token + ' ';
        callbacks.onToken(currentText);
        await new Promise(resolve => setTimeout(resolve, 50)); // 50ms delay per token
      }

      callbacks.onComplete(response);

    } catch (error) {
      callbacks.onError(error);
    }
  }

  static async generateDraft(
    history: { role: string; content: string }[]
  ): Promise<{ title: string; insight: string; lesson: string; keywords: string[] }> {
    const { ProviderManagementService } = await import('./provider-management-service');
    const settings = ProviderManagementService.getActiveProviderSettings();
    const { GHOSTWRITER_AGENT_PROMPT } = await import('@/lib/agents/ghostwriter');

    if (!settings.apiKey) throw new Error("AI Provider not configured");

    const systemMessage = { role: 'system', content: GHOSTWRITER_AGENT_PROMPT };
    // Filter out system messages from history if any (though usually none in history array passed)
    const sanitizedHistory = history.filter(m => m.role !== 'system');

    const messages = [systemMessage, ...sanitizedHistory];

    const response = await this.generateResponse({
      apiKey: settings.apiKey,
      baseUrl: settings.baseUrl,
      model: settings.modelName,
      messages: messages
    });

    try {
      // Attempt to parse JSON
      // Clean up potential markdown code blocks
      const jsonString = response.replace(/```json\n?|\n?```/g, '').trim();
      return JSON.parse(jsonString);
    } catch (e) {
      console.error("Failed to parse Ghostwriter response:", response);
      throw new Error("Failed to generate valid draft");
    }
  }
}
