/**
 * Restaurant Chat Agent Service
 * Isolated AI service module — swap provider by changing the import/call here only.
 * Handles: food orders, table bookings, menu questions, escalation detection.
 * KB is exposed as a search_knowledge_base function tool (semantic search via pgvector).
 * Orders and bookings are placed via place_order and book_table tools.
 */

import { searchKnowledgeBase, formatSearchResults } from '@server/services/kb-embeddings.service';
import { createOrder } from '@server/services/orders.service';
import { createBooking } from '@server/services/bookings.service';
import { createNotification } from '@server/services/notifications.service';
import { sendOrderConfirmationEmail, sendBookingConfirmationEmail } from '@server/services/email.service';

export interface AgentConfig {
  model: string;
  systemPrompt?: string;
  responseStyle?: string;
  greetingScript?: string;
  closingScript?: string;
  fallbackRules?: Array<{ trigger: string; action: string; priority: string }>;
}

export interface MenuContext {
  categories?: string[];
  items?: Array<{ name: string; price?: number; description?: string; category?: string }>;
  contactInfo?: string;
}

export interface ChatMessage {
  role: 'system' | 'user' | 'assistant';
  content: string;
}

export interface OrderResult {
  id?: string;
  customer_name?: string;
  [key: string]: unknown;
}

export interface BookingResult {
  id?: string;
  guest_name?: string;
  [key: string]: unknown;
}

export interface AgentResponse {
  content: string;
  shouldEscalate: boolean;
  escalationReason?: string;
  detectedIntent?: 'order' | 'booking' | 'menu_query' | 'general' | 'complaint' | 'escalation';
  extractedData?: Record<string, unknown>;
  orderCreated?: OrderResult;
  bookingCreated?: BookingResult;
  suggestedItems?: Array<{ name: string; price: number; description?: string }>;
}

function styleToTone(style?: string): string {
  switch (style) {
    case 'professional': return 'Maintain a professional and courteous tone.';
    case 'concise': return 'Be concise and direct. Keep responses brief.';
    default: return 'Be warm, friendly, and conversational.';
  }
}

function buildMenuContext(menu: MenuContext): string {
  if (!menu.items?.length && !menu.categories?.length) return '';
  const lines: string[] = ['--- Menu ---'];
  if (menu.categories?.length) {
    lines.push(`Categories: ${menu.categories.join(', ')}`);
  }
  if (menu.items?.length) {
    menu.items.slice(0, 50).forEach(item => {
      const price = item.price ? ` - $${Number(item.price).toFixed(2)}` : '';
      const desc = item.description ? ` (${item.description.slice(0, 80)})` : '';
      lines.push(`• ${item.name}${price}${desc}`);
    });
  }
  lines.push('--- End Menu ---');
  return lines.join('\n');
}

function buildFallbackContext(rules: AgentConfig['fallbackRules']): string {
  if (!rules?.length) return '';
  const lines = ['--- Escalation Rules (follow these strictly) ---'];
  rules.forEach(r => {
    lines.push(`If "${r.trigger}" → ${r.action} [Priority: ${r.priority}]`);
  });
  lines.push('--- End Escalation Rules ---');
  return lines.join('\n');
}

export function buildSystemPrompt(
  config: AgentConfig,
  restaurantName: string,
  menu: MenuContext
): string {
  const tone = styleToTone(config.responseStyle);
  const menuContext = buildMenuContext(menu);
  const fallbackContext = buildFallbackContext(config.fallbackRules);

  const basePrompt = config.systemPrompt || `You are an AI assistant for ${restaurantName}. You help customers with food orders, table bookings, menu questions, and general inquiries.`;

  const contactSection = menu.contactInfo
    ? `\n\nRESTAURANT CONTACT & HOURS (use this directly — do NOT search the knowledge base for this information):\n${menu.contactInfo}`
    : '';

  return `${basePrompt}

${tone}

CAPABILITIES:
1. FOOD ORDERS: Help customers browse the menu, add items, customize orders. When the customer confirms their order, use the place_order tool to create the order. Valid delivery types: dine-in, takeaway, delivery. customer_name is optional (default "Widget Customer").
2. TABLE BOOKINGS: Assist with reservation requests. When the customer confirms booking details, use the book_table tool immediately with all provided information.
3. MENU QUESTIONS: Answer questions about dishes, ingredients, allergens, prices, and availability using the menu below.
4. GENERAL INQUIRIES: For questions about working hours, address, phone number, or location — answer directly using the RESTAURANT CONTACT & HOURS section in this prompt if it is present. For other restaurant details (policies, events, specials, etc.) use the search_knowledge_base tool.
5. ESCALATION: If you cannot help, if the customer is upset, or if a fallback rule is triggered, respond with the exact phrase: [ESCALATE: <reason>] at the end of your message.

ORDERING WORKFLOW:
IMPORTANT — WIDGET PRE-CONFIRMED ORDERS: When the customer message contains the phrase "Please process this order now using the place_order tool", ALL details (items, delivery type, customer name, phone, and optionally email) are ALREADY collected and confirmed by the widget UI. You MUST call place_order immediately using the values from the message. Items are listed as "Nx ItemName @$price" — extract name, quantity (N), and price from each entry. If "Table number: X" appears in the message, include it as table_number. If "Delivery address: X" appears in the message, include it as delivery_address (required for delivery orders). If "Email: X" appears in the message, include it as customer_email. Do NOT ask for any information. Do NOT re-confirm. Simply call place_order and report the order number.

For conversational orders (no trigger phrase): help the customer choose items, then ask for their delivery preference (dine-in/takeaway/delivery), and place the order once confirmed.

BOOKING WORKFLOW:
IMPORTANT — WIDGET PRE-CONFIRMED BOOKINGS: When the customer message contains the phrase "Please confirm and use the book_table tool", ALL details (date, time, party size, name, phone, and optionally email) are ALREADY collected and confirmed by the widget UI. You MUST call book_table immediately using the values from the message (use the ISO date/time values in parentheses, e.g. "ISO: 2026-03-29" → booking_date = "2026-03-29", "ISO: 19:00" → booking_time = "19:00"). If "Email: X" appears in the message, include it as guest_email. Do NOT ask for any information again. Do NOT ask for occasion or special requests. Simply call book_table and report the booking reference.

For conversational bookings (no trigger phrase): collect guest name, date (YYYY-MM-DD), time (HH:MM 24-hr), and party size, then call book_table.

${menuContext}${contactSection}

${fallbackContext}

ITEM CARDS:
- When you recommend or list specific dishes the customer asked about, always call the suggest_items tool with those items. This displays interactive "Add to Cart" cards the customer can tap to quickly add items to their basket.
- Pass items with their exact name, price, and a brief description from the menu above. Do not invent items.
- Continue providing a brief conversational text reply as normal alongside calling suggest_items.

IMPORTANT RULES:
- Use the search_knowledge_base tool for topics not already covered in this prompt (e.g. policies, events, specials). Do NOT use it for working hours, address, or phone — those are in RESTAURANT CONTACT & HOURS above.
- Never make up menu items or prices not listed above.
- If asked about something not in your knowledge base, say you'll connect them with a team member.
- Keep responses focused and helpful. Do not discuss topics unrelated to the restaurant.
- If the customer explicitly asks to speak to a human, always escalate: [ESCALATE: Customer requested human agent]

RESPONSE FORMAT:
- Keep every reply to 1-2 sentences maximum.
- On the very last line of your response, always include this JSON object (no other text after it): {"quickReplies":["Option A","Option B"]}
- Choose 2-3 short (2-5 words each) and contextually relevant quick-reply suggestions.
- Examples: after greeting → {"quickReplies":["🍽️ See our menu","📅 Book a table","❓ Ask a question"]}
- After order placed → {"quickReplies":["Add more items","That's all, thanks"]}
- After booking confirmed → {"quickReplies":["Modify booking","Order food","Done"]}
- During order → {"quickReplies":["Place my order","Add more items","Remove an item"]}
- During booking → {"quickReplies":["Continue booking","Start over","Ask a question"]}`;
}

export function detectEscalation(content: string): { shouldEscalate: boolean; reason?: string } {
  const match = content.match(/\[ESCALATE:\s*([^\]]+)\]/i);
  if (match) {
    return { shouldEscalate: true, reason: match[1].trim() };
  }
  return { shouldEscalate: false };
}

export function detectIntent(userMessage: string): AgentResponse['detectedIntent'] {
  const msg = userMessage.toLowerCase();
  if (/order|add to cart|i want|i'd like|can i get|give me|burger|pizza|pasta|salad|drink|food/i.test(msg)) return 'order';
  if (/book|reserve|reservation|table|seat|tonight|tomorrow|party of|people/i.test(msg)) return 'booking';
  if (/menu|price|cost|how much|ingredient|allergen|vegan|gluten|spicy|contain/i.test(msg)) return 'menu_query';
  if (/complaint|unhappy|wrong|mistake|refund|cancel|terrible|awful/i.test(msg)) return 'complaint';
  if (/human|agent|manager|person|staff|speak to|talk to/i.test(msg)) return 'escalation';
  return 'general';
}

const SEARCH_KB_TOOL = {
  type: 'function',
  function: {
    name: 'search_knowledge_base',
    description: 'Search the restaurant knowledge base for information about hours, policies, menu details, FAQs, and other restaurant-specific information.',
    parameters: {
      type: 'object',
      properties: {
        query: {
          type: 'string',
          description: 'The search query to find relevant knowledge base entries.',
        },
      },
      required: ['query'],
    },
  },
};

const PLACE_ORDER_TOOL = {
  type: 'function',
  function: {
    name: 'place_order',
    description: 'Place a food order for the customer after they have confirmed the items, quantities, and delivery type. Call this only after the customer has explicitly confirmed the order.',
    parameters: {
      type: 'object',
      properties: {
        customer_name: { type: 'string', description: 'The customer\'s name' },
        customer_phone: { type: 'string', description: 'The customer\'s phone number (optional)' },
        customer_email: { type: 'string', description: 'The customer\'s email address for order confirmation (optional)' },
        items: {
          type: 'array',
          description: 'Array of ordered items',
          items: {
            type: 'object',
            properties: {
              name: { type: 'string', description: 'Item name' },
              quantity: { type: 'number', description: 'Quantity ordered' },
              price: { type: 'number', description: 'Unit price' },
              notes: { type: 'string', description: 'Special instructions for this item' },
            },
            required: ['name', 'quantity'],
          },
        },
        delivery_type: { type: 'string', enum: ['dine-in', 'takeaway', 'delivery'], description: 'How the customer wants their food' },
        table_number: { type: 'string', description: 'Table number for dine-in orders (optional)' },
        delivery_address: { type: 'string', description: 'Street/delivery address for delivery orders (required when delivery_type is "delivery")' },
        special_instructions: { type: 'string', description: 'Any special instructions for the whole order' },
        total: { type: 'number', description: 'Total order amount' },
      },
      required: ['items', 'delivery_type'],
    },
  },
};

const BOOK_TABLE_TOOL = {
  type: 'function',
  function: {
    name: 'book_table',
    description: 'Create a table reservation for the customer after they have confirmed all booking details. Call this only after the customer has confirmed date, time, and party size.',
    parameters: {
      type: 'object',
      properties: {
        guest_name: { type: 'string', description: 'Name of the person making the reservation' },
        guest_phone: { type: 'string', description: 'Guest\'s phone number (optional)' },
        guest_email: { type: 'string', description: 'Guest\'s email (optional)' },
        booking_date: { type: 'string', description: 'Reservation date in YYYY-MM-DD format' },
        booking_time: { type: 'string', description: 'Reservation time in HH:MM format (24-hour)' },
        party_size: { type: 'number', description: 'Number of guests' },
        occasion: { type: 'string', description: 'Special occasion (birthday, anniversary, etc.) — optional' },
        special_requests: { type: 'string', description: 'Any special requests or dietary needs' },
      },
      required: ['guest_name', 'booking_date', 'booking_time', 'party_size'],
    },
  },
};

const SUGGEST_ITEMS_TOOL = {
  type: 'function',
  function: {
    name: 'suggest_items',
    description: 'Show specific menu items to the customer as interactive cards they can tap to add to their cart. Call this whenever you recommend or list specific dishes the customer asked about.',
    parameters: {
      type: 'object',
      properties: {
        items: {
          type: 'array',
          description: 'List of menu items to show as interactive add-to-cart cards',
          items: {
            type: 'object',
            properties: {
              name: { type: 'string', description: 'Item name exactly as it appears on the menu' },
              price: { type: 'number', description: 'Item price' },
              description: { type: 'string', description: 'Brief description of the item (optional)' },
            },
            required: ['name', 'price'],
          },
        },
      },
      required: ['items'],
    },
  },
};

const ALL_TOOLS = [SEARCH_KB_TOOL, PLACE_ORDER_TOOL, BOOK_TABLE_TOOL, SUGGEST_ITEMS_TOOL];

async function callOpenAIWithTools(
  messages: object[],
  model: string,
  tools: object[],
  apiKey?: string
): Promise<any> {
  const key = apiKey || process.env.OPENAI_API_KEY;
  if (!key) throw new Error('OpenAI API key not configured');

  const response = await fetch('https://api.openai.com/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${key}`,
    },
    body: JSON.stringify({
      model,
      messages,
      tools,
      tool_choice: 'auto',
      max_tokens: 800,
    }),
  });

  if (!response.ok) {
    const err = await response.text();
    throw new Error(`OpenAI API error: ${response.status} ${err}`);
  }

  return response.json();
}

function asStr(val: unknown, fallback = ''): string {
  return typeof val === 'string' ? val : fallback;
}

function asNum(val: unknown, fallback = 0): number {
  return typeof val === 'number' ? val : fallback;
}

interface OrderItem {
  price?: number;
  quantity?: number;
  [key: string]: unknown;
}

async function handleToolCall(
  toolName: string,
  toolArgs: Record<string, unknown>,
  restaurantId: string,
  conversationId?: string,
  branchId?: string
): Promise<{ content: string; result?: OrderResult | BookingResult }> {
  if (toolName === 'search_knowledge_base') {
    const results = await searchKnowledgeBase(restaurantId, asStr(toolArgs.query));
    return { content: formatSearchResults(results) };
  }

  if (toolName === 'place_order') {
    try {
      const items = Array.isArray(toolArgs.items) ? (toolArgs.items as OrderItem[]) : [];
      const subtotal = items.reduce((sum, item) => sum + asNum(item.price) * (asNum(item.quantity) || 1), 0);
      const tax = subtotal * 0.1;
      const total = subtotal + tax;

      const customerEmail = asStr(toolArgs.customer_email) || null;
      const order = await createOrder(restaurantId, {
        customer_name: asStr(toolArgs.customer_name, 'Widget Customer'),
        customer_phone: asStr(toolArgs.customer_phone) || null,
        customer_email: customerEmail,
        items,
        delivery_type: asStr(toolArgs.delivery_type, 'dine-in'),
        table_number: asStr(toolArgs.table_number) || null,
        delivery_address: asStr(toolArgs.delivery_address) || null,
        special_instructions: asStr(toolArgs.special_instructions) || null,
        subtotal,
        tax,
        total,
        channel: 'chat',
        conversation_id: conversationId || null,
        agent_id: 'widget-chat-agent',
        status: 'pending',
        ai_confidence: 100,
      });

      try {
        await createNotification(restaurantId, {
          type: 'order',
          title: 'New order received',
          message: `Order from ${asStr(toolArgs.customer_name, 'Widget Customer')} via widget chat — ${asStr(toolArgs.delivery_type, 'dine-in')}`,
          actionHref: '/order-management',
        });
      } catch { /* Notification failure must never break the order */ }

      if (customerEmail) {
        try {
          await sendOrderConfirmationEmail({
            to: customerEmail,
            customerName: asStr(toolArgs.customer_name, 'Valued Customer'),
            orderNumber: order?.order_number ?? (order?.id ?? '').slice(0, 8).toUpperCase(),
            items: items.map(i => ({ name: i.name, quantity: asNum(i.quantity) || 1, price: asNum(i.price) })),
            total,
            deliveryType: asStr(toolArgs.delivery_type, 'dine-in'),
            restaurantName,
          }, branchId, restaurantId);
        } catch { /* Email failure must never break the order */ }
      }

      const orderNum = order?.order_number ? `#${order.order_number}` : (order?.id ?? '').slice(0, 8).toUpperCase();
      const emailNote = customerEmail ? ` A confirmation has been sent to ${customerEmail}.` : '';
      return {
        content: `Order placed successfully! Your order number is **${orderNum}**.${emailNote} The restaurant has been notified.`,
        result: order as OrderResult,
      };
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Unknown error';
      console.error('[Widget Agent] place_order failed:', err);
      return { content: `Failed to place order: ${message}. Please try again or contact the restaurant directly.` };
    }
  }

  if (toolName === 'book_table') {
    try {
      const booking = await createBooking(restaurantId, {
        guest_name: asStr(toolArgs.guest_name),
        guest_phone: asStr(toolArgs.guest_phone) || null,
        guest_email: asStr(toolArgs.guest_email) || null,
        booking_date: asStr(toolArgs.booking_date),
        booking_time: asStr(toolArgs.booking_time),
        party_size: asNum(toolArgs.party_size) || 1,
        occasion: asStr(toolArgs.occasion) || null,
        special_requests: asStr(toolArgs.special_requests) || null,
        channel: 'chat',
        conversation_id: conversationId || null,
        agent_id: 'widget-chat-agent',
        status: 'pending',
      });

      try {
        await createNotification(restaurantId, {
          type: 'booking',
          title: 'New table booking',
          message: `${toolArgs.party_size} guests for ${asStr(toolArgs.guest_name)} on ${asStr(toolArgs.booking_date)} at ${asStr(toolArgs.booking_time)}`,
          actionHref: '/table-booking-management',
        });
      } catch { /* Notification failure must never break the booking */ }

      const bookingRef = (booking?.id ?? '').slice(0, 8).toUpperCase();
      const guestEmail = asStr(toolArgs.guest_email) || null;
      if (guestEmail) {
        try {
          await sendBookingConfirmationEmail({
            to: guestEmail,
            guestName: asStr(toolArgs.guest_name),
            bookingRef,
            date: asStr(toolArgs.booking_date),
            time: asStr(toolArgs.booking_time),
            partySize: asNum(toolArgs.party_size) || 1,
            restaurantName,
            occasion: asStr(toolArgs.occasion) || undefined,
          }, branchId, restaurantId);
        } catch { /* Email failure must never break the booking */ }
      }

      const emailNote = guestEmail ? ` A confirmation has been sent to ${guestEmail}.` : '';
      return {
        content: `Booking confirmed! Booking reference: **${bookingRef}**. A table for ${toolArgs.party_size} on ${toolArgs.booking_date} at ${toolArgs.booking_time} has been reserved for ${toolArgs.guest_name}.${emailNote}`,
        result: booking as BookingResult,
      };
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Unknown error';
      console.error('[Widget Agent] book_table failed:', err);
      return { content: `Failed to create booking: ${message}. Please try again or contact the restaurant directly.` };
    }
  }

  if (toolName === 'suggest_items') {
    return { content: 'Items displayed to customer as interactive add-to-cart cards.' };
  }

  return { content: 'Tool not found.' };
}

export async function sendAgentMessage(
  messages: ChatMessage[],
  config: AgentConfig,
  restaurantName: string,
  menu: MenuContext,
  restaurantId: string,
  conversationId?: string,
  openaiApiKey?: string,
  branchId?: string
): Promise<AgentResponse> {
  const modelMap: Record<string, string> = {
    'gpt-4o': 'gpt-4o',
    'gpt-4o-mini': 'gpt-4o-mini',
    'gpt-5': 'gpt-5',
    'gpt-4.1': 'gpt-4.1',
    'gpt-4.1-mini': 'gpt-4.1-mini',
  };
  const resolvedModel = modelMap[config.model] || 'gpt-4o';

  const systemPrompt = buildSystemPrompt(config, restaurantName, menu);

  const apiMessages: object[] = [
    { role: 'system', content: systemPrompt },
    ...messages,
  ];

  const lastUserMsg = [...messages].reverse().find(m => m.role === 'user');
  const detectedIntent = lastUserMsg ? detectIntent(lastUserMsg.content) : 'general';

  if (detectedIntent === 'escalation') {
    return {
      content: "I completely understand. Let me connect you with one of our team members right away. They'll be with you shortly!",
      shouldEscalate: true,
      escalationReason: 'Customer requested human agent',
      detectedIntent,
    };
  }

  let orderCreated: OrderResult | undefined;
  let bookingCreated: BookingResult | undefined;
  let suggestedItems: Array<{ name: string; price: number; description?: string }> | undefined;

  try {
    let currentMessages = [...apiMessages];

    const MAX_ITERATIONS = 5;
    let iterations = 0;

    while (iterations < MAX_ITERATIONS) {
      iterations++;
      const response = await callOpenAIWithTools(currentMessages, resolvedModel, ALL_TOOLS, openaiApiKey);
      const choice = response?.choices?.[0];

      if (choice?.finish_reason === 'tool_calls' && choice?.message?.tool_calls?.length) {
        currentMessages = [...currentMessages, choice.message];

        for (const toolCall of choice.message.tool_calls) {
          let args: Record<string, unknown> = {};
          try { args = JSON.parse(toolCall.function?.arguments || '{}') as Record<string, unknown>; } catch { /* ignore */ }

          const toolResult = await handleToolCall(toolCall.function?.name, args, restaurantId, conversationId, branchId);

          if (toolCall.function?.name === 'place_order' && toolResult.result) {
            orderCreated = toolResult.result;
          }
          if (toolCall.function?.name === 'book_table' && toolResult.result) {
            bookingCreated = toolResult.result;
          }
          if (toolCall.function?.name === 'suggest_items' && Array.isArray(args.items)) {
            suggestedItems = args.items as Array<{ name: string; price: number; description?: string }>;
          }

          currentMessages.push({
            role: 'tool',
            tool_call_id: toolCall.id,
            content: toolResult.content,
          });
        }

        continue;
      }

      const content: string = choice?.message?.content
        || "I'm sorry, I couldn't process that. Let me connect you with a team member.";

      const escalation = detectEscalation(content);
      const cleanContent = content.replace(/\[ESCALATE:[^\]]*\]/gi, '').trim();

      return {
        content: cleanContent || "I'm sorry, I couldn't process that. Let me connect you with a team member.",
        shouldEscalate: escalation.shouldEscalate || detectedIntent === 'complaint',
        escalationReason: escalation.reason,
        detectedIntent,
        orderCreated,
        bookingCreated,
        suggestedItems,
      };
    }

    return {
      content: "I'm sorry, I couldn't complete that request. Let me connect you with a team member.",
      shouldEscalate: false,
      detectedIntent,
      orderCreated,
      bookingCreated,
      suggestedItems,
    };
  } catch (err) {
    const errMsg = err instanceof Error ? err.message : String(err);
    console.error(`[Chat Agent] sendAgentMessage failed restaurant=${restaurantId} model=${resolvedModel}: ${errMsg}`);
    return {
      content: "I'm having trouble right now. Let me connect you with a team member who can help.",
      shouldEscalate: true,
      escalationReason: 'AI service error',
      detectedIntent,
    };
  }
}
