import { NextRequest, NextResponse } from 'next/server';
import { initDatabase } from '@server/db/init';
import { db } from '@server/db/drizzle';
import { sql, type SQL } from 'drizzle-orm';
import { validateTwilioSignature } from '@server/services/voice/twilio-signature';

import { childLogger } from '@server/logger';
import { wrapRouteHandler } from '@server/logger/request';
const log = childLogger('webhook.twilio.status');

async function resolveAuthToken(callSid: string): Promise<string | null> {
  const envToken = process.env.TWILIO_AUTH_TOKEN;
  if (!callSid) return envToken || null;

  try {
    /* raw: SELECT restaurant_id FROM sip_call_sessions WHERE sip_call_id = $1 LIMIT 1 */
    const { rows } = await db.execute(sql`
      SELECT restaurant_id FROM sip_call_sessions WHERE sip_call_id = ${callSid} LIMIT 1
    `);
    const session = rows[0] as { restaurant_id: string } | undefined;
    if (session) {
      /* raw: SELECT twilio_auth_token, twilio_connected FROM telephone_settings WHERE restaurant_id = $1 */
      const { rows: settingsRows } = await db.execute(sql`
        SELECT twilio_auth_token, twilio_connected FROM telephone_settings WHERE restaurant_id = ${session.restaurant_id}
      `);
      const settingsRow = settingsRows[0] as { twilio_auth_token: string | null; twilio_connected: boolean } | undefined;
      if (settingsRow?.twilio_connected && settingsRow.twilio_auth_token) {
        try {
          const { decrypt } = await import('@server/utils/crypto');
          return decrypt(settingsRow.twilio_auth_token);
        } catch {
          return settingsRow.twilio_auth_token;
        }
      }
    }
  } catch (err: unknown) {
    log.warn({ err }, 'auth token lookup error');
  }

  return envToken || null;
}

async function handleTwilioStatus(req: NextRequest) {
  await initDatabase();

  const params: Record<string, string> = {};
  try {
    const formData = await req.formData();
    formData.forEach((val, key) => { params[key] = String(val); });
  } catch {
    return NextResponse.json({ error: 'Invalid request body' }, { status: 400 });
  }

  const callSid = params['CallSid'] || '';
  const signature = req.headers.get('x-twilio-signature');
  const authToken = await resolveAuthToken(callSid);

  let signatureValid: boolean | null = null;
  if (process.env.NODE_ENV === 'production') {
    if (!signature || !authToken) {
      log.info({ signatureValid: false, event: params['CallStatus'] || 'status', id: callSid || null, type: 'webhook.received' }, 'webhook.received');
      return new NextResponse('Forbidden', { status: 403 });
    }
    const url = `https://${req.headers.get('host')}${req.nextUrl.pathname}${req.nextUrl.search}`;
    signatureValid = validateTwilioSignature(authToken, signature, url, params);
    if (!signatureValid) {
      log.info({ signatureValid: false, event: params['CallStatus'] || 'status', id: callSid || null, type: 'webhook.received' }, 'webhook.received');
      return new NextResponse('Forbidden', { status: 403 });
    }
  } else if (signature && authToken) {
    const url = `https://${req.headers.get('host')}${req.nextUrl.pathname}${req.nextUrl.search}`;
    signatureValid = validateTwilioSignature(authToken, signature, url, params);
    if (!signatureValid) {
      log.info({ signatureValid: false, event: params['CallStatus'] || 'status', id: callSid || null, type: 'webhook.received' }, 'webhook.received');
      return new NextResponse('Forbidden', { status: 403 });
    }
  }
  log.info(
    { signatureValid, event: params['CallStatus'] || 'status', id: callSid || null, type: 'webhook.received' },
    'webhook.received'
  );

  const callStatus = params['CallStatus'] || '';
  const callDuration = parseInt(params['CallDuration'] || '0', 10) || 0;
  const recordingUrl = params['RecordingUrl'] || null;

  if (!callSid) {
    return NextResponse.json({ received: true });
  }

  const terminalStatuses = ['completed', 'failed', 'busy', 'no-answer', 'canceled'];

  if (terminalStatuses.includes(callStatus)) {
    const dbStatus = callStatus;
    const updates: SQL[] = [sql`status = ${dbStatus}`, sql`ended_at = NOW()`];

    if (callDuration > 0) {
      updates.push(sql`duration_seconds = ${callDuration}`);
    }
    if (recordingUrl) {
      updates.push(sql`recording_url = ${recordingUrl}`);
    }

    /* raw: UPDATE sip_call_sessions SET ... WHERE sip_call_id = $N AND status IN ('active', 'ringing') */
    await db.execute(sql`
      UPDATE sip_call_sessions SET ${sql.join(updates, sql`, `)}
      WHERE sip_call_id = ${callSid} AND status IN ('active', 'ringing')
    `);

    /* raw: SELECT restaurant_id, direction, caller_number, callee_number, escalated_to_human, started_at FROM sip_call_sessions WHERE sip_call_id = $1 LIMIT 1 */
    const { rows: sessionRows } = await db.execute(sql`
      SELECT restaurant_id, direction, caller_number, callee_number, escalated_to_human, started_at
      FROM sip_call_sessions WHERE sip_call_id = ${callSid} LIMIT 1
    `);
    const session = sessionRows[0] as {
      restaurant_id: string;
      direction: string;
      caller_number: string | null;
      callee_number: string | null;
      escalated_to_human: boolean;
      started_at: string;
    } | undefined;

    if (session) {
      /* raw: INSERT INTO public.call_logs (...) VALUES (...) ON CONFLICT (call_sid) DO UPDATE ... */
      await db.execute(sql`
        INSERT INTO public.call_logs
          (restaurant_id, call_sid, direction, from_number, to_number,
           status, duration_seconds, recording_url,
           ai_handled, escalated_to_human, started_at, ended_at, created_at, updated_at)
        VALUES (${session.restaurant_id},${callSid},${session.direction},${session.caller_number},${session.callee_number},${dbStatus},${callDuration},${recordingUrl},${!session.escalated_to_human},${session.escalated_to_human},${session.started_at},NOW(),${session.started_at},NOW())
        ON CONFLICT (call_sid) DO UPDATE SET
          status = EXCLUDED.status,
          duration_seconds = EXCLUDED.duration_seconds,
          recording_url = COALESCE(EXCLUDED.recording_url, call_logs.recording_url),
          ended_at = NOW(),
          updated_at = NOW()
      `);
    }
  }

  return NextResponse.json({ received: true });
}

export const POST = wrapRouteHandler((req: Request) => handleTwilioStatus(req as NextRequest));
