/**
 * Call Log Analytics Edge Function
 *
 * Analyzes call transcripts using Google Gemini 2.5 Flash via OpenRouter.
 * The LLM uses MCP tools to search for related products, orders, and customers.
 *
 * Triggered:
 * - Automatically after VAPI webhook inserts a call log (non-blocking)
 * - Manually via API for retry functionality
 *
 * Authentication: Internal API key (Bearer token)
 */

import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
import { createClient, SupabaseClient } from 'https://esm.sh/@supabase/supabase-js@2';
import {
  requireInternalApiKey,
  createInternalApiKeyErrorResponse
} from '../_shared/internal-api-key-auth.ts';
import {
  chatCompletion,
  runConversationWithTools,
  parseJsonContent,
  getMessageContent,
  Tool,
  ToolCall,
  ChatMessage
} from '../_shared/openrouter-chat.ts';

const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
  'Access-Control-Allow-Methods': 'POST, OPTIONS',
};

// Model to use for analytics
const ANALYTICS_MODEL = 'google/gemini-2.5-flash';

// Platform to MCP endpoint mapping
const PLATFORM_MCP_ENDPOINTS: Record<string, string> = {
  shopify: 'mcp-shopify',
  woocommerce: 'mcp-woocommerce',
  shoprenter: 'mcp-shoprenter',
};

// Tool name prefixes by platform
const PLATFORM_TOOL_PREFIXES: Record<string, string> = {
  shopify: 'shopify',
  woocommerce: 'woocommerce',
  shoprenter: 'shoprenter',
};

/**
 * Analytics response structure from LLM
 */
interface AnalyticsResponse {
  call_outcome: string;
  summary: string;
  satisfaction_score: number;
  related_customers: Array<{
    id: string;
    email?: string;
    name?: string;
    phone?: string;
    match_reason: string;
  }>;
  related_orders: Array<{
    id: string;
    order_number: string;
    status?: string;
    total?: string;
    match_reason: string;
  }>;
  related_products: Array<{
    id: string;
    name: string;
    sku?: string;
    price?: string;
    match_reason: string;
  }>;
}

// Default system prompt (used as fallback if not configured in database)
const DEFAULT_SYSTEM_PROMPT = `You are a call log analyst for an e-commerce customer service system. Your task is to analyze call transcripts and extract structured insights.

## Your Capabilities
You have access to tools to search the store's database for:
- Products (search by name, SKU, or description)
- Orders (search by order number or customer email)
- Customers (search by email or phone)

## Important Context
- Store ID (shop_id): {{SHOP_ID}}
- Platform: {{PLATFORM_NAME}}
- Always use shop_id: "{{SHOP_ID}}" when calling any tool

## Your Tasks
1. Read the transcript carefully
2. Determine the call outcome based on the conversation
3. Write a brief summary (1-3 sentences) of what the call was about
4. Use tools to identify any products, orders, or customers mentioned in the conversation
5. Return your analysis in JSON format

## Order-Related Conversations - IMPORTANT
When the conversation involves an order (order inquiry, order status, delivery question, return request, etc.):
1. First, search for the order using the order number or customer email mentioned
2. From the order results, extract the customer information (customer_id, email, name)
3. Then search for the customer record using that information to get full customer details
4. Include BOTH the order AND the customer in your related entities

This ensures we link the call to both the specific order AND the customer record for better tracking.

Example flow for order-related calls:
- Customer mentions order #1234
- You call search_orders with order_number="#1234"
- Order result contains customer_email="john@example.com"
- You call search_customers with email="john@example.com"
- Include both the order and customer in your response

## Call Outcome Values
You MUST choose one of these exact values for call_outcome:
{{CALL_OUTCOME_VALUES}}

Choose the most appropriate outcome based on the conversation:
- "order_inquiry": Customer asking about order status, delivery, tracking
- "product_inquiry": Customer asking about products, availability, pricing
- "order_placed": Customer placed a new order during the call
- "complaint": Customer expressing dissatisfaction or filing a complaint
- "return_request": Customer requesting to return or exchange products
- "general_question": General questions not related to orders/products
- "callback_requested": Customer requested a callback
- "no_answer": Call was not answered
- "voicemail": Reached voicemail
- "wrong_number": Wrong number or invalid call
- "resolved": Customer's issue was successfully resolved
- "escalated": Issue was escalated to a supervisor/specialist
- "abandoned": Call was abandoned/disconnected

## Tool Usage Guidelines
- Only search for entities actually mentioned in the transcript
- If a customer mentions an order number (like #1234 or Order 1234), search for it
- If a customer provides their email or phone, search for their customer record
- If specific products are discussed by name or SKU, search for them
- Don't make up search queries - only search for things explicitly mentioned
- For order-related calls: ALWAYS try to find the associated customer from the order data

## Customer Satisfaction Score
You MUST evaluate the customer's satisfaction level based on the transcript and provide a score from 0 to 100:
- 0-20: Very dissatisfied (angry, frustrated, threatening to leave)
- 21-40: Dissatisfied (unhappy, disappointed, complaining)
- 41-60: Neutral (matter-of-fact, no strong emotions)
- 61-80: Satisfied (pleased, thankful, positive tone)
- 81-100: Very satisfied (enthusiastic, praising, highly grateful)

Consider these factors:
- Customer's tone and language throughout the call
- Whether their issue was resolved
- How they responded to the assistance provided
- Any explicit expressions of satisfaction or dissatisfaction
- The overall flow and outcome of the conversation

## JSON Response Format
You MUST respond with valid JSON in this exact format:
{
  "call_outcome": "<one of the allowed values>",
  "summary": "<brief 1-3 sentence summary>",
  "satisfaction_score": <number from 0-100>,
  "related_customers": [
    {
      "id": "<customer id>",
      "email": "<email if found>",
      "name": "<name if found>",
      "phone": "<phone if found>",
      "match_reason": "<why this customer was identified>"
    }
  ],
  "related_orders": [
    {
      "id": "<order id>",
      "order_number": "<order number>",
      "status": "<order status if found>",
      "total": "<order total if found>",
      "match_reason": "<why this order was identified>"
    }
  ],
  "related_products": [
    {
      "id": "<product id>",
      "name": "<product name>",
      "sku": "<SKU if found>",
      "price": "<price if found>",
      "match_reason": "<why this product was identified>"
    }
  ]
}

If no related customers/orders/products were found or mentioned, use empty arrays [].
IMPORTANT: Return ONLY the JSON object, no markdown code blocks or additional text.`;

/**
 * Fetch system prompt from database config
 */
async function fetchSystemPromptTemplate(supabase: SupabaseClient): Promise<string> {
  try {
    const { data, error } = await supabase
      .from('system_config')
      .select('value')
      .eq('key', 'call_analytics_system_prompt')
      .single();

    if (error || !data?.value) {
      console.log('[Analytics] Using default system prompt (not found in system_config)');
      return DEFAULT_SYSTEM_PROMPT;
    }

    console.log('[Analytics] Using system prompt from system_config');
    return data.value;
  } catch (err) {
    console.error('[Analytics] Error fetching system prompt:', err);
    return DEFAULT_SYSTEM_PROMPT;
  }
}

/**
 * Build the system prompt for call analysis by replacing placeholders
 *
 * Available placeholders:
 * - {{SHOP_ID}} - The store's UUID
 * - {{PLATFORM_NAME}} - The e-commerce platform (shopify, woocommerce, shoprenter)
 * - {{CALL_OUTCOME_VALUES}} - List of valid call outcome enum values
 */
function buildSystemPrompt(
  promptTemplate: string,
  callOutcomeValues: string[],
  shopId: string,
  platformName: string
): string {
  const callOutcomeList = callOutcomeValues.map(v => `- "${v}"`).join('\n');

  return promptTemplate
    .replace(/\{\{SHOP_ID\}\}/g, shopId)
    .replace(/\{\{PLATFORM_NAME\}\}/g, platformName)
    .replace(/\{\{CALL_OUTCOME_VALUES\}\}/g, callOutcomeList);
}

/**
 * Build tool definitions for the LLM
 */
function buildTools(platformName: string, shopId: string): Tool[] {
  const prefix = PLATFORM_TOOL_PREFIXES[platformName] || 'shopify';

  return [
    {
      type: 'function',
      function: {
        name: 'search_products',
        description: `Search for products in the store by name, SKU, or description. Returns product details including id, name, SKU, price. Always use shop_id: "${shopId}"`,
        parameters: {
          type: 'object',
          properties: {
            name: {
              type: 'string',
              description: 'Product name to search for (partial match)'
            },
            sku: {
              type: 'string',
              description: 'Product SKU to search for (exact match)'
            }
          }
        }
      }
    },
    {
      type: 'function',
      function: {
        name: 'search_orders',
        description: `Search for orders by order number or customer email. Returns order details including id, order number, status, total. Always use shop_id: "${shopId}"`,
        parameters: {
          type: 'object',
          properties: {
            order_number: {
              type: 'string',
              description: 'Order number to look up (e.g., "#1001" or "1001")'
            },
            customer_email: {
              type: 'string',
              description: 'Customer email to search orders for'
            }
          }
        }
      }
    },
    {
      type: 'function',
      function: {
        name: 'search_customers',
        description: `Search for customers by email or phone number. Returns customer details including id, name, email, phone. Always use shop_id: "${shopId}"`,
        parameters: {
          type: 'object',
          properties: {
            email: {
              type: 'string',
              description: 'Customer email address'
            },
            phone: {
              type: 'string',
              description: 'Customer phone number'
            }
          }
        }
      }
    }
  ];
}

/**
 * Tool call log entry for debugging and auditing
 */
interface ToolCallLogEntry {
  timestamp: string;
  tool_name: string;
  mcp_tool_name: string;
  parameters: Record<string, unknown>;
  result: unknown;
  duration_ms: number;
  success: boolean;
  error?: string;
}

// Global array to collect tool call logs during analysis
let toolCallLogs: ToolCallLogEntry[] = [];

/**
 * Execute a tool call by calling the appropriate MCP endpoint
 */
async function executeMcpTool(
  toolCall: ToolCall,
  shopId: string,
  platformName: string,
  internalApiKey: string,
  supabaseUrl: string
): Promise<unknown> {
  const startTime = Date.now();
  const mcpEndpoint = PLATFORM_MCP_ENDPOINTS[platformName];
  if (!mcpEndpoint) {
    return { error: `Unsupported platform: ${platformName}` };
  }

  const toolPrefix = PLATFORM_TOOL_PREFIXES[platformName] || 'shopify';
  const args = JSON.parse(toolCall.function.arguments || '{}');

  // Map our simplified tool names to MCP tool names
  let mcpToolName: string;
  const mcpArgs: Record<string, unknown> = { shop_id: shopId, ...args };

  switch (toolCall.function.name) {
    case 'search_products':
      mcpToolName = `${toolPrefix}_get_products`;
      mcpArgs.limit = 5;
      break;
    case 'search_orders':
      if (args.order_number) {
        mcpToolName = `${toolPrefix}_get_order`;
        // Clean order number (remove # prefix if present)
        // Use order_id for ShopRenter, order_name for Shopify
        const cleanOrderNumber = args.order_number.replace(/^#/, '');
        if (platformName === 'shoprenter') {
          mcpArgs.order_id = cleanOrderNumber;
        } else {
          mcpArgs.order_name = cleanOrderNumber;
        }
        delete mcpArgs.order_number;
      } else {
        mcpToolName = `${toolPrefix}_list_orders`;
        mcpArgs.limit = 5;
      }
      break;
    case 'search_customers':
      mcpToolName = `${toolPrefix}_get_customer`;
      break;
    default:
      return { error: `Unknown tool: ${toolCall.function.name}` };
  }

  console.log(`[Analytics] Calling MCP tool: ${mcpToolName} with args:`, mcpArgs);

  // Build JSON-RPC request for MCP
  const jsonRpcRequest = {
    jsonrpc: '2.0',
    id: crypto.randomUUID(),
    method: 'tools/call',
    params: {
      name: mcpToolName,
      arguments: mcpArgs
    }
  };

  // Prepare log entry
  const logEntry: ToolCallLogEntry = {
    timestamp: new Date().toISOString(),
    tool_name: toolCall.function.name,
    mcp_tool_name: mcpToolName,
    parameters: mcpArgs,
    result: null,
    duration_ms: 0,
    success: false
  };

  try {
    const response = await fetch(`${supabaseUrl}/functions/v1/${mcpEndpoint}`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${internalApiKey}`,
        'Accept': 'text/event-stream'
      },
      body: JSON.stringify(jsonRpcRequest)
    });

    if (!response.ok) {
      const errorText = await response.text();
      console.error(`[Analytics] MCP call failed (${response.status}):`, errorText);
      logEntry.duration_ms = Date.now() - startTime;
      logEntry.error = `MCP call failed: ${response.status}`;
      logEntry.result = { error: errorText };
      toolCallLogs.push(logEntry);
      return { error: `MCP call failed: ${response.status}` };
    }

    // Parse SSE response
    const text = await response.text();

    // SSE format: "data: {...}\n\n"
    const lines = text.split('\n');
    for (const line of lines) {
      if (line.startsWith('data: ')) {
        const data = JSON.parse(line.substring(6));
        if (data.result) {
          // Extract text content from MCP response
          const content = data.result.content;
          let resultValue: unknown = data.result;

          if (Array.isArray(content) && content.length > 0) {
            const textContent = content.find((c: any) => c.type === 'text');
            if (textContent?.text) {
              resultValue = textContent.text;
            }
          }

          // Log successful call
          logEntry.duration_ms = Date.now() - startTime;
          logEntry.success = true;
          logEntry.result = resultValue;
          toolCallLogs.push(logEntry);
          console.log(`[Analytics] MCP tool ${mcpToolName} completed in ${logEntry.duration_ms}ms`);

          return resultValue;
        }
        if (data.error) {
          logEntry.duration_ms = Date.now() - startTime;
          logEntry.error = data.error.message || 'MCP tool error';
          logEntry.result = data.error;
          toolCallLogs.push(logEntry);
          return { error: data.error.message || 'MCP tool error' };
        }
      }
    }

    logEntry.duration_ms = Date.now() - startTime;
    logEntry.error = 'No valid response from MCP';
    toolCallLogs.push(logEntry);
    return { error: 'No valid response from MCP' };
  } catch (error) {
    console.error(`[Analytics] MCP call error:`, error);
    logEntry.duration_ms = Date.now() - startTime;
    logEntry.error = error instanceof Error ? error.message : 'MCP call failed';
    toolCallLogs.push(logEntry);
    return { error: error instanceof Error ? error.message : 'MCP call failed' };
  }
}

/**
 * Main analytics function
 */
async function analyzeCallLog(
  supabase: SupabaseClient,
  callLogId: string,
  internalApiKey: string
): Promise<void> {
  const supabaseUrl = Deno.env.get('SUPABASE_URL')!;

  // Reset tool call logs for this analysis run
  toolCallLogs = [];

  console.log(`[Analytics] Starting analysis for call log: ${callLogId}`);

  // Update status to running
  await supabase
    .from('call_logs')
    .update({
      analytics_status: 'running',
      analytics_started_at: new Date().toISOString(),
      analytics_error: null
    })
    .eq('id', callLogId);

  try {
    // Fetch call log with store info
    const { data: callLog, error: callLogError } = await supabase
      .from('call_logs')
      .select('id, transcript, store_id')
      .eq('id', callLogId)
      .single();

    if (callLogError || !callLog) {
      throw new Error(`Call log not found: ${callLogId}`);
    }

    if (!callLog.transcript) {
      throw new Error('Call log has no transcript to analyze');
    }

    if (!callLog.store_id) {
      throw new Error('Call log has no associated store');
    }

    // Fetch store info
    const { data: store, error: storeError } = await supabase
      .from('stores')
      .select('id, platform_name, store_name')
      .eq('id', callLog.store_id)
      .single();

    if (storeError || !store) {
      throw new Error(`Store not found: ${callLog.store_id}`);
    }

    const platformName = (store.platform_name || 'shopify').toLowerCase();
    console.log(`[Analytics] Store: ${store.store_name}, Platform: ${platformName}`);

    // Fetch call outcome enum values dynamically
    const { data: enumValues, error: enumError } = await supabase
      .rpc('get_call_outcome_enum_values');

    if (enumError) {
      console.error('[Analytics] Failed to fetch enum values:', enumError);
    }

    const callOutcomeValues = enumValues || [
      'pending', 'resolved', 'interested', 'potential', 'not_interested',
      'no_answer', 'voicemail', 'busy', 'callback_requested', 'false'
    ];

    console.log(`[Analytics] Call outcome values: ${callOutcomeValues.join(', ')}`);

    // Fetch system prompt template from database
    const promptTemplate = await fetchSystemPromptTemplate(supabase);

    // Build system prompt and tools
    const systemPrompt = buildSystemPrompt(promptTemplate, callOutcomeValues, store.id, platformName);
    const tools = buildTools(platformName, store.id);

    // Prepare messages
    const messages: ChatMessage[] = [
      { role: 'system', content: systemPrompt },
      { role: 'user', content: `Please analyze this call transcript:\n\n${callLog.transcript}` }
    ];

    // Create tool executor
    const toolExecutor = async (toolCall: ToolCall): Promise<unknown> => {
      return executeMcpTool(toolCall, store.id, platformName, internalApiKey, supabaseUrl);
    };

    // Run conversation with tools
    const response = await runConversationWithTools(
      {
        model: ANALYTICS_MODEL,
        messages,
        tools,
        response_format: { type: 'json_object' },
        temperature: 0.2,
        max_tokens: 4096
      },
      toolExecutor,
      5 // Max 5 tool call iterations
    );

    // Parse response
    const content = getMessageContent(response);
    const analyticsResult = parseJsonContent<AnalyticsResponse>(content);

    if (!analyticsResult) {
      throw new Error('Failed to parse LLM response as JSON');
    }

    console.log(`[Analytics] Analysis complete. Outcome: ${analyticsResult.call_outcome}`);

    // Validate call_outcome
    if (!callOutcomeValues.includes(analyticsResult.call_outcome)) {
      console.warn(`[Analytics] Invalid call_outcome "${analyticsResult.call_outcome}", defaulting to "pending"`);
      analyticsResult.call_outcome = 'pending';
    }

    // Build full analytics JSON with tool call logs
    const fullAnalyticsJson = {
      ...analyticsResult,
      _meta: {
        model: ANALYTICS_MODEL,
        analyzed_at: new Date().toISOString(),
        tool_calls_count: toolCallLogs.length,
        total_tool_duration_ms: toolCallLogs.reduce((sum, log) => sum + log.duration_ms, 0)
      },
      _tool_calls: toolCallLogs
    };

    console.log(`[Analytics] Tool calls made: ${toolCallLogs.length}`);

    // Update call log with results
    const { error: updateError } = await supabase
      .from('call_logs')
      .update({
        analytics_status: 'completed',
        analytics_completed_at: new Date().toISOString(),
        outcome: analyticsResult.call_outcome,
        summary: analyticsResult.summary,
        satisfaction_score: analyticsResult.satisfaction_score,
        related_customers: analyticsResult.related_customers,
        related_orders: analyticsResult.related_orders,
        related_products: analyticsResult.related_products,
        llm_analytics_json: fullAnalyticsJson
      })
      .eq('id', callLogId);

    if (updateError) {
      throw new Error(`Failed to update call log: ${updateError.message}`);
    }

    console.log(`[Analytics] Successfully updated call log ${callLogId}`);

  } catch (error) {
    console.error(`[Analytics] Error analyzing call log:`, error);

    // Build partial analytics JSON with tool call logs for debugging
    const failedAnalyticsJson = {
      _meta: {
        model: ANALYTICS_MODEL,
        analyzed_at: new Date().toISOString(),
        tool_calls_count: toolCallLogs.length,
        total_tool_duration_ms: toolCallLogs.reduce((sum, log) => sum + log.duration_ms, 0),
        failed: true,
        error: error instanceof Error ? error.message : 'Unknown error'
      },
      _tool_calls: toolCallLogs
    };

    // Update status to failed
    await supabase
      .from('call_logs')
      .update({
        analytics_status: 'failed',
        analytics_completed_at: new Date().toISOString(),
        analytics_error: error instanceof Error ? error.message : 'Unknown error',
        llm_analytics_json: failedAnalyticsJson
      })
      .eq('id', callLogId);

    throw error;
  }
}

serve(async (req) => {
  // Handle CORS preflight
  if (req.method === 'OPTIONS') {
    return new Response('ok', { headers: corsHeaders });
  }

  try {
    // Only accept POST requests
    if (req.method !== 'POST') {
      return new Response(
        JSON.stringify({ error: 'Method not allowed' }),
        { status: 405, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
      );
    }

    // Create Supabase admin client
    const supabaseUrl = Deno.env.get('SUPABASE_URL')!;
    const supabaseKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!;
    const supabase = createClient(supabaseUrl, supabaseKey);

    // Validate internal API key
    const authResult = await requireInternalApiKey(req, supabase);
    if (!authResult.valid) {
      const errorResponse = createInternalApiKeyErrorResponse(authResult);
      return new Response(errorResponse.body, {
        status: 401,
        headers: { ...corsHeaders, 'Content-Type': 'application/json' }
      });
    }

    // Get the internal API key for MCP calls
    const authHeader = req.headers.get('Authorization') || req.headers.get('authentication');
    const internalApiKey = authHeader?.replace(/^Bearer\s+/i, '') || '';

    // Parse request body
    const body = await req.json();
    const { call_log_id } = body;

    if (!call_log_id) {
      return new Response(
        JSON.stringify({ error: 'Missing required field: call_log_id' }),
        { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
      );
    }

    // Validate UUID format
    const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
    if (!uuidRegex.test(call_log_id)) {
      return new Response(
        JSON.stringify({ error: 'Invalid call_log_id format. Must be a valid UUID.' }),
        { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
      );
    }

    // Run analytics (can be non-blocking if needed)
    await analyzeCallLog(supabase, call_log_id, internalApiKey);

    return new Response(
      JSON.stringify({
        success: true,
        message: 'Call log analysis completed',
        call_log_id
      }),
      { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
    );

  } catch (error) {
    console.error('[Analytics] Request error:', error);

    return new Response(
      JSON.stringify({
        error: 'Analytics processing failed',
        details: error instanceof Error ? error.message : 'Unknown error'
      }),
      { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
    );
  }
});
