import { NextResponse } from 'next/server';
import { withErrorHandler } from '@server/middleware/withErrorHandler';
import {
  verifyWidgetToken,
  appendWidgetMessage,
  getWidgetSettings,
  updateWidgetConversationStatus,
} from '@server/services/widget.service';
import { sendAgentMessage } from '@server/integrations/ai/restaurantChatAgent';
import { resolveApiKey } from '@server/services/provider-keys.service';
import { db, conversations, aiAgents, llmModels, aiAgentConfigs, menuItems as menuItemsTable, restaurants, branches } from '@server/db/drizzle';
import { eq, and, sql } from 'drizzle-orm';
import { initDatabase } from '@server/db/init';
import { withWidgetCors, widgetOptionsResponse } from '../_cors';
import { createNotification } from '@server/services/notifications.service';
import { broadcastEscalation } from '@server/utils/escalationBus';

export async function OPTIONS() { return widgetOptionsResponse(); }

export const POST = withWidgetCors(withErrorHandler(async (req: Request) => {
  const body = await req.json().catch(() => ({}));
  const { sessionToken, message, history } = body as {
    sessionToken?: string;
    message?: string;
    history?: Array<{ role: string; content: string }>;
  };

  if (!sessionToken || !message) {
    return NextResponse.json({ error: 'sessionToken and message are required' }, { status: 400 });
  }

  const session = verifyWidgetToken(sessionToken);
  if (!session) {
    return NextResponse.json({ error: 'Invalid or expired session token' }, { status: 401 });
  }

  const { restaurantId, conversationId } = session;

  await initDatabase();

  const settings = await getWidgetSettings(restaurantId);
  if (!settings?.is_enabled) {
    return NextResponse.json({ error: 'Chat widget is not enabled' }, { status: 403 });
  }

  await appendWidgetMessage(conversationId, restaurantId, 'customer', message);

  interface AgentRow {
    system_prompt: string | null;
    greeting_script: string | null;
    closing_script: string | null;
    fallback_rules: Array<{ trigger: string; action: string; priority: string }> | null;
    model_id: string | null;
    agent_name: string | null;
    branch_id: string | null;
  }

  /* raw: SELECT agent from ai_agents with JOIN on llm_models — conditional on widget agent_id */
  const agentResult = settings.agent_id
    ? await db.execute(sql`
        SELECT a.system_prompt, a.greeting_script, a.closing_script, a.fallback_rules,
               lm.model_id, a.name as agent_name, a.branch_id
        FROM ai_agents a
        LEFT JOIN llm_models lm ON lm.id = a.llm_model_id
        WHERE a.id = ${settings.agent_id} AND a.restaurant_id = ${restaurantId} AND a.is_active = true
      `)
    : await db.execute(sql`
        SELECT a.system_prompt, a.greeting_script, a.closing_script, a.fallback_rules,
               lm.model_id, a.name as agent_name, a.branch_id
        FROM ai_agents a
        LEFT JOIN llm_models lm ON lm.id = a.llm_model_id
        WHERE a.restaurant_id = ${restaurantId} AND a.is_active = true AND 'chat' = ANY(a.channels)
        ORDER BY a.is_default DESC, a.created_at LIMIT 1
      `);

  const chatAgentRow = (agentResult.rows[0] as unknown as AgentRow | undefined) ?? null;

  const [totalAgentsResult, legacyConfigResult, menuResult, restaurantResult, branchResult] = await Promise.all([
    db.execute(sql`SELECT COUNT(*) AS count FROM ai_agents WHERE restaurant_id = ${restaurantId} AND is_active = true`),
    db.execute(sql`SELECT * FROM ai_agent_configs WHERE restaurant_id = ${restaurantId} LIMIT 1`),
    db.execute(sql`SELECT mi.name, mi.price, mi.description FROM menu_items mi WHERE mi.restaurant_id = ${restaurantId} AND mi.is_available = true LIMIT 80`),
    db.execute(sql`SELECT name FROM restaurants WHERE id = ${restaurantId} LIMIT 1`),
    db.execute(sql`SELECT name, phone, address, hours, timezone FROM branches WHERE restaurant_id = ${restaurantId} AND is_active = true LIMIT 5`),
  ]);

  const totalAgentsRow = totalAgentsResult.rows[0] as { count: string } | undefined;
  const legacyConfig = legacyConfigResult.rows[0] as Record<string, unknown> | undefined;
  const hasAnyAgents = parseInt(totalAgentsRow?.count ?? '0', 10) > 0;

  let agentConfig: {
    model: string;
    systemPrompt?: string;
    responseStyle?: string;
    greetingScript?: string;
    closingScript?: string;
    fallbackRules?: Array<{ trigger: string; action: string; priority: string }>;
  } | null = null;

  if (chatAgentRow) {
    agentConfig = {
      model: chatAgentRow.model_id || (legacyConfig?.model as string) || 'gpt-4o',
      systemPrompt: chatAgentRow.system_prompt ?? (legacyConfig?.system_prompt as string),
      responseStyle: (legacyConfig?.response_style as string) || 'friendly',
      greetingScript: chatAgentRow.greeting_script ?? (legacyConfig?.greeting_script as string),
      closingScript: chatAgentRow.closing_script ?? (legacyConfig?.closing_script as string),
      fallbackRules: Array.isArray(chatAgentRow.fallback_rules)
        ? chatAgentRow.fallback_rules
        : (Array.isArray(legacyConfig?.fallback_rules) ? legacyConfig.fallback_rules as Array<{ trigger: string; action: string; priority: string }> : []),
    };
  } else if (!hasAnyAgents && legacyConfig) {
    agentConfig = {
      model: (legacyConfig.model as string) || 'gpt-4o',
      systemPrompt: legacyConfig.system_prompt as string,
      responseStyle: (legacyConfig.response_style as string) || 'friendly',
      greetingScript: legacyConfig.greeting_script as string,
      closingScript: legacyConfig.closing_script as string,
      fallbackRules: Array.isArray(legacyConfig.fallback_rules) ? legacyConfig.fallback_rules as Array<{ trigger: string; action: string; priority: string }> : [],
    };
  } else {
    agentConfig = { model: 'gpt-4o', responseStyle: 'friendly' };
  }

  interface MenuItemRow { name: string; price: number; description: string }
  interface BranchRow {
    name: string;
    phone: string | null;
    address: string | null;
    hours: Record<string, { open: string; close: string; closed?: boolean }> | null;
    timezone: string | null;
  }

  const DAY_NAMES: Record<string, string> = {
    monday: 'Monday', tuesday: 'Tuesday', wednesday: 'Wednesday',
    thursday: 'Thursday', friday: 'Friday', saturday: 'Saturday', sunday: 'Sunday',
  };

  function formatBranchHours(hours: BranchRow['hours'], timezone: string | null): string | null {
    if (!hours || typeof hours !== 'object') return null;
    const lines: string[] = [];
    for (const day of ['monday','tuesday','wednesday','thursday','friday','saturday','sunday']) {
      const slot = hours[day];
      if (!slot) continue;
      if (slot.closed) {
        lines.push(`${DAY_NAMES[day]}: Closed`);
      } else if (slot.open && slot.close) {
        lines.push(`${DAY_NAMES[day]}: ${slot.open} – ${slot.close}`);
      }
    }
    if (lines.length === 0) return null;
    const tzNote = timezone ? ` (${timezone})` : '';
    return `Working Hours${tzNote}:\n${lines.join(', ')}`;
  }

  const branchData = branchResult.rows as unknown as BranchRow[];
  let contactInfo: string | undefined;
  if (branchData.length > 0) {
    const lines = ['--- Restaurant Contact Info ---'];
    branchData.forEach(b => {
      const parts = [`Branch: ${b.name}`];
      if (b.phone) parts.push(`Phone: ${b.phone}`);
      if (b.address) parts.push(`Address: ${b.address}`);
      lines.push(parts.join(' | '));
      const hoursStr = formatBranchHours(b.hours, b.timezone);
      if (hoursStr) lines.push(hoursStr);
    });
    lines.push('--- End Contact Info ---');
    contactInfo = lines.join('\n');
  }

  const menuContext = {
    items: (menuResult.rows as unknown as MenuItemRow[]).map(r => ({
      name: r.name,
      price: r.price,
      description: r.description,
    })),
    contactInfo,
  };

  const restaurantName = (restaurantResult.rows[0] as { name: string } | undefined)?.name || 'Our Restaurant';
  const chatHistory: Array<{ role: 'user' | 'assistant'; content: string }> = [
    ...(Array.isArray(history) ? history.map(m => ({
      role: m.role as 'user' | 'assistant',
      content: m.content,
    })) : []),
    { role: 'user', content: message },
  ];

  const openaiApiKey = await resolveApiKey(restaurantId, 'openai');

  const agentLabel = chatAgentRow?.agent_name || (settings.agent_id ? 'pinned-agent' : 'auto-selected');
  console.log(`[Widget Chat] restaurant=${restaurantId} agent="${agentLabel}" model=${agentConfig.model} historyLen=${chatHistory.length} menuItems=${menuContext.items?.length ?? 0}`);

  let agentResponse: Awaited<ReturnType<typeof sendAgentMessage>>;
  try {
    agentResponse = await sendAgentMessage(
      chatHistory,
      agentConfig,
      restaurantName,
      menuContext,
      restaurantId,
      conversationId,
      openaiApiKey ?? undefined,
      chatAgentRow?.branch_id ?? undefined
    );
  } catch (err) {
    const errMsg = err instanceof Error ? err.message : String(err);
    console.error(`[Widget Chat] sendAgentMessage failed: restaurant=${restaurantId} agent="${agentLabel}" model=${agentConfig.model}: ${errMsg}`);
    throw err;
  }

  console.log(`[Widget Chat] intent=${agentResponse.detectedIntent} escalate=${agentResponse.shouldEscalate} replyLen=${agentResponse.content?.length ?? 0}`);

  let displayContent = agentResponse.content || '';
  let quickReplies: string[] = [];
  const qrMatch = displayContent.match(/\{"quickReplies"\s*:\s*(\[[^\]]*\])\s*\}/);
  if (qrMatch) {
    try {
      const parsed = JSON.parse(qrMatch[0]) as { quickReplies?: unknown };
      if (Array.isArray(parsed.quickReplies)) {
        quickReplies = parsed.quickReplies.filter((r): r is string => typeof r === 'string').slice(0, 4);
      }
    } catch {
    }
    displayContent = displayContent.replace(/\{"quickReplies"\s*:\s*\[[^\]]*\]\s*\}/g, '').trim();
  }

  await appendWidgetMessage(conversationId, restaurantId, 'ai', displayContent);

  if (agentResponse.shouldEscalate) {
    await updateWidgetConversationStatus(conversationId, restaurantId, 'human');
    try {
      await createNotification(restaurantId, {
        type: 'escalation',
        title: 'Customer requesting human agent',
        message: agentResponse.escalationReason || 'A chat customer has asked to speak with a staff member.',
        actionHref: '/conversations',
      });
    } catch { /* Notification failure must never block the response */ }
    broadcastEscalation({
      restaurantId,
      conversationId,
      reason: agentResponse.escalationReason || 'Customer requested human agent',
    });
  }

  const topicMap: Record<string, string> = {
    order: 'Food Order',
    booking: 'Table Booking',
    menu_query: 'Menu Inquiry',
    complaint: 'Customer Complaint',
    escalation: 'Human Agent Request',
    general: 'General Inquiry',
  };

  if (agentResponse.detectedIntent && agentResponse.detectedIntent !== 'general') {
    await updateWidgetConversationStatus(
      conversationId,
      restaurantId,
      agentResponse.shouldEscalate ? 'human' : 'ai',
      topicMap[agentResponse.detectedIntent]
    );
  }

  const collectedCustomerName =
    agentResponse.orderCreated?.customer_name ||
    agentResponse.bookingCreated?.guest_name;
  if (collectedCustomerName && typeof collectedCustomerName === 'string') {
    await db.update(conversations)
      .set({ customerName: collectedCustomerName, updatedAt: sql`NOW()` })
      .where(and(
        eq(conversations.id, conversationId),
        eq(conversations.restaurantId, restaurantId),
        eq(conversations.customerName, 'Website Visitor'),
      ));
  }

  return NextResponse.json({
    reply: displayContent,
    quickReplies: quickReplies,
    shouldEscalate: agentResponse.shouldEscalate,
    escalationReason: agentResponse.escalationReason,
    detectedIntent: agentResponse.detectedIntent,
    orderCreated: agentResponse.orderCreated || null,
    bookingCreated: agentResponse.bookingCreated || null,
    suggestedItems: agentResponse.suggestedItems || null,
  });
}));
