/**
 * OpenRouter AI Service
 *
 * Provides AI chat completion via OpenRouter's API.
 * Supports text and structured JSON responses for the WhatsApp AI agent.
 * Config loaded from DB (system_settings) with env var fallbacks.
 */

import type {
  OpenRouterConfig,
  ChatMessage,
  OpenRouterResponse,
  StructuredAIResponse,
} from '@/lib/types/twilio-whatsapp.types';
import { systemSettingsService, SETTING_KEYS } from '@/lib/services/system-settings.service';
import { logger } from '@/lib/logger';

const OPENROUTER_API_URL = 'https://openrouter.ai/api/v1/chat/completions';
const REQUEST_TIMEOUT_MS = 30_000;

class OpenRouterService {
  /**
   * Load OpenRouter configuration from DB settings with env fallbacks
   */
  async getConfig(): Promise<OpenRouterConfig> {
    const apiKey =
      (await systemSettingsService.get(SETTING_KEYS.OPENROUTER_API_KEY)) ||
      process.env.OPENROUTER_API_KEY ||
      '';

    const model =
      (await systemSettingsService.get(SETTING_KEYS.OPENROUTER_MODEL)) ||
      process.env.OPENROUTER_DEFAULT_MODEL ||
      'anthropic/claude-sonnet-4';

    const maxTokens = parseInt(process.env.OPENROUTER_MAX_TOKENS || '1024', 10);
    const temperature = parseFloat(process.env.OPENROUTER_TEMPERATURE || '0.3');

    return { apiKey, model, maxTokens, temperature };
  }

  /**
   * Send a chat completion request to OpenRouter
   */
  async chatCompletion(
    messages: ChatMessage[],
    options?: {
      model?: string;
      maxTokens?: number;
      temperature?: number;
      responseFormat?: 'text' | 'json';
    }
  ): Promise<OpenRouterResponse> {
    const config = await this.getConfig();

    if (!config.apiKey) {
      return {
        success: false,
        content: '',
        model: options?.model || config.model,
        usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
        error: 'OpenRouter API key not configured',
      };
    }

    const model = options?.model || config.model;
    const maxTokens = options?.maxTokens || config.maxTokens;
    const temperature = options?.temperature ?? config.temperature;

    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);

    try {
      const response = await fetch(OPENROUTER_API_URL, {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${config.apiKey}`,
          'HTTP-Referer': process.env.NEXT_PUBLIC_APP_URL || 'https://mawidi.com',
          'X-Title': 'Mawidi AI Agent',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          model,
          messages,
          max_tokens: maxTokens,
          temperature,
          ...(options?.responseFormat === 'json'
            ? { response_format: { type: 'json_object' } }
            : {}),
        }),
        signal: controller.signal,
      });

      if (!response.ok) {
        const errorBody = await response.text();
        logger.error('OpenRouter API error', {
          status: response.status,
          body: errorBody,
          model,
        });
        return {
          success: false,
          content: '',
          model,
          usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
          error: `OpenRouter API returned ${response.status}: ${errorBody}`,
        };
      }

      const data = await response.json();
      const content = data.choices?.[0]?.message?.content || '';
      const usage = data.usage || {};

      return {
        success: true,
        content,
        model: data.model || model,
        usage: {
          promptTokens: usage.prompt_tokens || 0,
          completionTokens: usage.completion_tokens || 0,
          totalTokens: usage.total_tokens || 0,
        },
      };
    } catch (err) {
      const isAbort =
        err instanceof DOMException && err.name === 'AbortError';
      const message = isAbort
        ? 'OpenRouter request timed out'
        : 'OpenRouter request failed';

      logger.error(message, { error: err, model });

      return {
        success: false,
        content: '',
        model,
        usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
        error: isAbort
          ? `Request timed out after ${REQUEST_TIMEOUT_MS}ms`
          : err instanceof Error
            ? err.message
            : String(err),
      };
    } finally {
      clearTimeout(timeout);
    }
  }

  /**
   * Send a chat completion expecting structured JSON back.
   * Parses the response into a StructuredAIResponse.
   * Falls back to a safe generic message if JSON parsing fails — NEVER sends
   * raw JSON content to end users.
   */
  async structuredCompletion(
    messages: ChatMessage[]
  ): Promise<StructuredAIResponse> {
    const result = await this.chatCompletion(messages, {
      responseFormat: 'json',
    });

    if (!result.success) {
      return {
        intent: 'general',
        response: result.error || 'AI service unavailable',
        language: 'en',
      };
    }

    // Helper: extract a JSON object from raw content via regex (handles models
    // that wrap JSON in prose or extra text)
    const extractJsonFromContent = (content: string): StructuredAIResponse | null => {
      const match = content.match(/\{[\s\S]*\}/);
      if (!match) return null;
      try {
        const extracted = JSON.parse(match[0]);
        // Only use if the extracted response is plain text, not another JSON blob
        if (
          extracted.response &&
          typeof extracted.response === 'string' &&
          !extracted.response.trim().startsWith('{')
        ) {
          return {
            intent: extracted.intent || 'general',
            response: extracted.response,
            language: extracted.language || 'en',
            action: extracted.action,
            collectedInfo: extracted.collectedInfo,
            missingFields: extracted.missingFields,
            guardrailTriggered: extracted.guardrailTriggered,
            guardrailReason: extracted.guardrailReason,
          };
        }
      } catch {
        // Extracted JSON also unparseable
      }
      return null;
    };

    try {
      // Strip markdown code fences and leading "json" prefix that some models add
      let raw = result.content.trim();
      if (raw.startsWith('```')) {
        raw = raw.replace(/^```(?:json)?\s*/, '').replace(/```\s*$/, '').trim();
      } else if (raw.toLowerCase().startsWith('json')) {
        raw = raw.replace(/^json\s*/i, '').trim();
      }

      const parsed = JSON.parse(raw);

      if (!parsed.intent || !parsed.response || !parsed.language) {
        logger.warn('Structured response missing required fields', {
          hasIntent: !!parsed.intent,
          hasResponse: !!parsed.response,
          hasLanguage: !!parsed.language,
        });

        // Ensure response is always a plain text string — never raw JSON
        const safeResponse =
          parsed.response && typeof parsed.response === 'string' && !parsed.response.trim().startsWith('{')
            ? parsed.response
            : 'I apologize for the inconvenience. Please try sending your message again.';

        return {
          intent: parsed.intent || 'general',
          response: safeResponse,
          language: parsed.language || 'en',
          action: parsed.action,
          collectedInfo: parsed.collectedInfo,
          missingFields: parsed.missingFields,
          guardrailTriggered: parsed.guardrailTriggered,
          guardrailReason: parsed.guardrailReason,
        };
      }

      return parsed as StructuredAIResponse;
    } catch {
      // Primary parse failed — attempt regex extraction from content
      const rescued = extractJsonFromContent(result.content);
      if (rescued) {
        logger.info('Structured response rescued via regex extraction', {
          contentLength: result.content.length,
        });
        return rescued;
      }

      // All JSON parsing failed — use the AI's raw text if it looks like a
      // genuine conversational response (not JSON/HTML/code), otherwise use
      // a safe fallback.  This prevents discarding useful AI-generated text
      // that simply wasn't wrapped in JSON.
      const raw = result.content.trim();
      const looksLikeRealText =
        raw.length > 10 &&
        raw.length < 2000 &&
        !raw.startsWith('{') &&
        !raw.startsWith('<') &&
        !raw.startsWith('```');

      if (looksLikeRealText) {
        logger.warn('Failed to parse structured AI response as JSON, using raw text as fallback', {
          contentLength: raw.length,
          contentPreview: raw.slice(0, 120),
        });
        // Detect language from content (Arabic chars = ar)
        const hasArabic = /[\u0600-\u06FF]/.test(raw);
        return {
          intent: 'general',
          response: raw,
          language: hasArabic ? 'ar' : 'en',
        };
      }

      logger.warn('Failed to parse structured AI response as JSON, using safe fallback', {
        contentLength: result.content.length,
        contentPreview: result.content.slice(0, 120),
      });
      return {
        intent: 'general',
        response: 'I apologize for the inconvenience. Please try sending your message again.',
        language: 'en',
      };
    }
  }

  /**
   * Quick check whether an API key is available (env or potentially in DB)
   */
  isConfigured(): boolean {
    return !!process.env.OPENROUTER_API_KEY;
  }
}

export const openRouterService = new OpenRouterService();
