import { NextResponse } from 'next/server';
import { withErrorHandler } from '@server/middleware/withErrorHandler';
import { withAuth, AuthedRequest } from '@server/middleware/withAuth';
import { ValidationError } from '@server/errors';
import { sendAgentMessage } from '@server/integrations/ai/restaurantChatAgent';
import { filterOrderableItems } from '@server/services/menu.service';
import { db } from '@server/db/drizzle';
import { sql } from 'drizzle-orm';
import { initDatabase } from '@server/db/init';
import { DEFAULT_LLM_MODEL } from '@server/config/ai-defaults';

interface ChatAgentRow {
  agent_id: string;
  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;
  capabilities: Record<string, boolean> | null;
  menu_category_ids: string[] | null;
}

export const POST = withErrorHandler(
  withAuth(async (req: AuthedRequest) => {
    let _body: Awaited<ReturnType<typeof req.json>>;
    try {
      _body = await req.json();
    } catch {
      throw new ValidationError('Request body must be valid JSON');
    }
    const { conversationId, message, history } = _body;
    const { restaurantId } = req.session;

    if (!restaurantId || !conversationId || !message) {
      return NextResponse.json({ error: 'Missing required fields' }, { status: 400 });
    }

    await initDatabase();

    const [chatAgentResult, totalAgentsResult, legacyConfigResult, menuResult, restaurantResult] = await Promise.all([
      /* raw: SELECT a.system_prompt, ... FROM ai_agents a LEFT JOIN llm_models lm ... WHERE a.restaurant_id = $1 ... */
      db.execute(sql`
        SELECT a.id AS agent_id, a.system_prompt, a.greeting_script, a.closing_script,
                a.fallback_rules, a.capabilities, a.menu_category_ids, lm.model_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
      `),
      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.id, mi.name, mi.price, mi.description, mi.category_id, mc.name AS category FROM menu_items mi LEFT JOIN menu_categories mc ON mc.id = mi.category_id WHERE mi.restaurant_id = ${restaurantId} AND mi.is_available = true LIMIT 60`),
      db.execute(sql`SELECT name FROM restaurants WHERE id = ${restaurantId} LIMIT 1`),
    ]);

    const chatAgentRow = chatAgentResult.rows[0] as unknown as ChatAgentRow | undefined;
    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 }>;
      agentId?: string | null;
      capabilities?: Record<string, boolean> | null;
      menuCategoryIds?: string[] | null;
    } | null = null;

    if (chatAgentRow) {
      agentConfig = {
        model: chatAgentRow.model_id || (legacyConfig?.model as string) || DEFAULT_LLM_MODEL,
        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 }> : []),
        agentId: chatAgentRow.agent_id ?? null,
        capabilities: chatAgentRow.capabilities ?? null,
        menuCategoryIds: Array.isArray(chatAgentRow.menu_category_ids) ? chatAgentRow.menu_category_ids : null,
      };
    } else if (!hasAnyAgents && legacyConfig) {
      agentConfig = {
        model: (legacyConfig.model as string) || DEFAULT_LLM_MODEL,
        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 }> : [],
      };
    }

    if (!agentConfig) {
      return NextResponse.json({
        reply: "I'm sorry, chat is not available at the moment. Please contact us by phone or try again later.",
        shouldEscalate: false,
        escalationReason: undefined,
        detectedIntent: 'general',
      });
    }

    interface MenuItemRow { id: string; name: string; price: number; description: string; category_id: string | null; category: string | null }
    // Filter the LLM-visible menu so disabled / out-of-stock / off-schedule
    // items disappear automatically. Also restrict to the agent's allowed
    // menu_category_ids whitelist (if set) so the LLM never sees, suggests
    // or describes items outside that scope. The place_order tool re-checks
    // category membership server-side regardless.
    const allowedCats = agentConfig.menuCategoryIds && agentConfig.menuCategoryIds.length
      ? new Set(agentConfig.menuCategoryIds) : null;
    const allMenuItems = (menuResult.rows as unknown as MenuItemRow[])
      .filter(r => !allowedCats || (r.category_id && allowedCats.has(r.category_id)))
      .map(r => ({
        id: r.id,
        name: r.name,
        price: r.price,
        description: r.description,
        category: r.category ?? undefined,
      }));
    const menuContext = {
      items: await filterOrderableItems(allMenuItems, null),
    };

    const restaurantName = (restaurantResult.rows[0] as { name: string } | undefined)?.name || 'Our Restaurant';

    const chatHistory = Array.isArray(history) ? history : [];

    const agentResponse = await sendAgentMessage(
      chatHistory,
      agentConfig,
      restaurantName,
      menuContext,
      restaurantId!,
      undefined,
      undefined,
      undefined,
      'widget',
      undefined,
      {
        agentId: agentConfig.agentId ?? null,
        capabilities: agentConfig.capabilities ?? null,
        menuCategoryIds: agentConfig.menuCategoryIds ?? null,
      }
    );

    return NextResponse.json({
      reply: agentResponse.content,
      shouldEscalate: agentResponse.shouldEscalate,
      escalationReason: agentResponse.escalationReason,
      detectedIntent: agentResponse.detectedIntent,
    });
  })
);
