import { NextResponse } from 'next/server';
import { getRouteByNumber, getContactByPhone, insertContact, insertMessage, insertEvent } from '@/lib/db';
import { getCloseBotClient } from '@/lib/closebot';
import { sendSms } from '@/lib/twilio-client';
import { broadcast } from '@/lib/sse';
import { generateId } from '@/lib/utils';

export const dynamic = 'force-dynamic';

interface Route {
  id: string;
  twilio_number: string;
  closebot_source_id: string;
  closebot_bot_id: string | null;
  bot_name: string | null;
  greeting_message: string | null;
  after_hours_reply: string | null;
  business_hours_json: string | null;
  active: number;
}

interface Contact {
  id: string;
  phone: string;
  name: string | null;
  status: string;
  assigned_route_id: string | null;
}

function twimlResponse(message: string): Response {
  const xml = `<?xml version="1.0" encoding="UTF-8"?>
<Response><Message>${escapeXml(message)}</Message></Response>`;
  return new Response(xml, {
    status: 200,
    headers: { 'Content-Type': 'text/xml' },
  });
}

function escapeXml(str: string): string {
  return str
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&apos;');
}

function isWithinBusinessHours(businessHoursJson: string | null): boolean {
  if (!businessHoursJson) return true; // No hours set = always open

  try {
    const hours = JSON.parse(businessHoursJson);
    const now = new Date();
    const dayNames = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
    const dayKey = dayNames[now.getDay()];
    const todayHours = hours[dayKey];

    // If no entry for today, business is closed
    if (!todayHours || !todayHours.start || !todayHours.end) return false;

    const currentMinutes = now.getHours() * 60 + now.getMinutes();

    const [startH, startM] = todayHours.start.split(':').map(Number);
    const [endH, endM] = todayHours.end.split(':').map(Number);
    const startMinutes = startH * 60 + startM;
    const endMinutes = endH * 60 + endM;

    return currentMinutes >= startMinutes && currentMinutes < endMinutes;
  } catch (error) {
    console.error('[inbound] Failed to parse business hours:', error);
    return true; // On error, default to open
  }
}

export async function POST(request: Request) {
  let from = '';
  let to = '';
  let body = '';
  let messageSid = '';

  try {
    // Twilio sends form-encoded data
    const formData = await request.formData();
    from = (formData.get('From') as string) || '';
    to = (formData.get('To') as string) || '';
    body = (formData.get('Body') as string) || '';
    messageSid = (formData.get('MessageSid') as string) || '';

    console.log(`[inbound] SMS from ${from} to ${to}: "${body.slice(0, 80)}"`);

    if (!from || !to) {
      console.error('[inbound] Missing From or To in webhook data');
      return new Response('', { status: 400 });
    }

    // ── Step 1: Look up route by the Twilio number ──
    const route = getRouteByNumber(to) as Route | undefined;

    if (!route) {
      console.warn(`[inbound] No active route found for number: ${to}`);
      return twimlResponse('This number is not currently configured. Please try again later.');
    }

    // ── Step 2: Check business hours ──
    if (!isWithinBusinessHours(route.business_hours_json)) {
      const afterHoursMsg = route.after_hours_reply
        || 'Thanks for reaching out! We are currently offline and will get back to you during business hours.';

      console.log(`[inbound] Outside business hours for route ${route.id}, sending after-hours reply`);

      // Send after-hours reply via Twilio API (not TwiML) for status tracking
      try {
        const twilioSid = await sendSms(from, to, afterHoursMsg);

        // Find or create contact for logging
        let contact = getContactByPhone(from) as Contact | undefined;
        if (!contact) {
          const contactId = generateId();
          insertContact({ id: contactId, phone: from, assigned_route_id: route.id, assigned_bot_id: route.closebot_bot_id || undefined });
          contact = { id: contactId, phone: from, name: null, status: 'new', assigned_route_id: route.id };
        }

        // Log the inbound message
        const inMsgId = generateId();
        insertMessage({
          id: inMsgId,
          contact_id: contact.id,
          route_id: route.id,
          direction: 'inbound',
          source: 'customer',
          body,
          twilio_sid: messageSid,
        });

        // Log the after-hours reply
        const outMsgId = generateId();
        insertMessage({
          id: outMsgId,
          contact_id: contact.id,
          route_id: route.id,
          direction: 'outbound',
          source: 'system',
          body: afterHoursMsg,
          twilio_sid: twilioSid,
        });

        insertEvent({
          type: 'after_hours',
          contact_id: contact.id,
          route_id: route.id,
          data_json: JSON.stringify({ inbound: body, reply: afterHoursMsg }),
        });

        broadcast('activity', {
          type: 'after_hours',
          contact_id: contact.id,
          phone: from,
          bot_name: route.bot_name,
          created_at: new Date().toISOString(),
        });
      } catch (smsError) {
        console.error('[inbound] Failed to send after-hours SMS:', smsError);
      }

      // Return empty TwiML (we already sent the reply via API)
      return new Response('<?xml version="1.0" encoding="UTF-8"?><Response></Response>', {
        status: 200,
        headers: { 'Content-Type': 'text/xml' },
      });
    }

    // ── Step 3: Find or create contact ──
    let contact = getContactByPhone(from) as Contact | undefined;
    if (!contact) {
      const contactId = generateId();
      insertContact({
        id: contactId,
        phone: from,
        assigned_route_id: route.id,
        assigned_bot_id: route.closebot_bot_id || undefined,
      });
      contact = { id: contactId, phone: from, name: null, status: 'new', assigned_route_id: route.id };
      console.log(`[inbound] Created new contact ${contactId} for ${from}`);
    }

    // ── Step 4: Log inbound message ──
    const msgId = generateId();
    insertMessage({
      id: msgId,
      contact_id: contact.id,
      route_id: route.id,
      direction: 'inbound',
      source: 'customer',
      body,
      twilio_sid: messageSid,
    });

    // ── Step 5: Insert event ──
    insertEvent({
      type: 'message_in',
      contact_id: contact.id,
      route_id: route.id,
      data_json: JSON.stringify({ body: body.slice(0, 200), twilio_sid: messageSid }),
    });

    // ── Step 6: Broadcast SSE ──
    broadcast('activity', {
      type: 'message_in',
      contact_id: contact.id,
      phone: from,
      name: contact.name,
      bot_name: route.bot_name,
      body: body.slice(0, 100),
      created_at: new Date().toISOString(),
    });

    broadcast('message', {
      id: msgId,
      contact_id: contact.id,
      route_id: route.id,
      direction: 'inbound',
      source: 'customer',
      body,
      created_at: new Date().toISOString(),
    });

    // ── Step 7: Forward to CloseBot ──
    try {
      const cb = getCloseBotClient();
      if (!cb.isConfigured) {
        console.error('[inbound] CloseBot not configured, cannot forward message');
      } else {
        const state = JSON.stringify({
          twilioNumber: to,
          phoneFrom: from,
          routeId: route.id,
          contactId: contact.id,
        });

        await cb.sendWebhookEvent(route.closebot_source_id, {
          type: 'message',
          contactId: contact.phone,
          message: body,
          state,
        });

        console.log(`[inbound] Forwarded to CloseBot source ${route.closebot_source_id}`);
      }
    } catch (cbError) {
      console.error('[inbound] Failed to forward to CloseBot:', cbError);
      // Don't fail the webhook — message is already logged
      // CloseBot being down shouldn't prevent Twilio from getting a 200
    }

    // Return empty TwiML (no auto-reply, CloseBot will respond via callback)
    return new Response('<?xml version="1.0" encoding="UTF-8"?><Response></Response>', {
      status: 200,
      headers: { 'Content-Type': 'text/xml' },
    });
  } catch (error) {
    console.error('[inbound] Unhandled error processing inbound SMS:', error);
    console.error(`[inbound] Context: from=${from}, to=${to}, body="${body.slice(0, 50)}"`);

    // Still return 200 to Twilio so it doesn't retry
    return new Response('<?xml version="1.0" encoding="UTF-8"?><Response></Response>', {
      status: 200,
      headers: { 'Content-Type': 'text/xml' },
    });
  }
}
