/**
 * Twilio WhatsApp Webhook — Real Estate
 *
 * Dedicated webhook endpoint for real estate organizations.
 * Mirrors the generic /api/webhooks/twilio-whatsapp but routes
 * messages through the real estate AI engine instead of the
 * generic appointment-booking engine.
 *
 * POST - Incoming messages (form-encoded from Twilio)
 * GET  - Health check
 *
 * Security:
 * - Rate limiting via Upstash Redis (100 req/min per IP)
 * - Twilio request signature validation per-org
 * - Input sanitization via DOMPurify
 */

import { NextRequest, NextResponse } from "next/server";
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
import { logger } from "@/lib/logger";
import { twilioWhatsAppService } from "@/lib/services/twilio-whatsapp.service";
import { realEstateAIEngine } from "@/lib/services/real-estate-ai-engine.service";
import { prisma } from "@/lib/db";
import {
  sanitizeChatMessage,
  sanitizePhoneNumber,
} from "@/lib/sanitizers/chat.sanitizer";
import type {
  ConversationContext,
  BookingFlowState,
} from "@/lib/types/twilio-whatsapp.types";
import {
  getLanguagePreference,
  setLanguagePreference,
} from "@/lib/services/whatsapp-language.service";
import { buildGreetingForOrg } from "@/lib/services/whatsapp-greeting.service";
import { qatariDialectService } from "@/lib/services/qatari-dialect.service";

// =============================================================================
// Rate Limiter
// =============================================================================

let webhookRateLimit: Ratelimit | null = null;

if (
  process.env.UPSTASH_REDIS_REST_URL &&
  process.env.UPSTASH_REDIS_REST_TOKEN
) {
  const redis = new Redis({
    url: process.env.UPSTASH_REDIS_REST_URL,
    token: process.env.UPSTASH_REDIS_REST_TOKEN,
  });

  webhookRateLimit = new Ratelimit({
    redis,
    limiter: Ratelimit.slidingWindow(100, "1 m"),
    analytics: true,
    prefix: "webhook-twilio-whatsapp-re",
  });
}

// =============================================================================
// Arabic detection helper
// =============================================================================

const ARABIC_RANGE = /[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF]/;

function detectLanguage(text: string): "en" | "ar" {
  const arabicChars = (text.match(new RegExp(ARABIC_RANGE.source, "g")) || [])
    .length;
  return arabicChars > text.length * 0.3 ? "ar" : "en";
}

// =============================================================================
// GET - Health check
// =============================================================================

export async function GET(): Promise<Response> {
  return NextResponse.json({
    status: "ok",
    service: "twilio-whatsapp-re-webhook",
    timestamp: new Date().toISOString(),
  });
}

// =============================================================================
// POST - Incoming Twilio WhatsApp messages (Real Estate)
// =============================================================================

export async function POST(request: NextRequest): Promise<Response> {
  try {
    // ── Rate limit ────────────────────────────────────────
    if (webhookRateLimit) {
      const ip =
        request.ip ??
        request.headers.get("x-forwarded-for") ??
        request.headers.get("x-real-ip") ??
        "unknown";

      const { success, limit, remaining, reset } =
        await webhookRateLimit.limit(ip);

      if (!success) {
        logger.security("RE Twilio webhook rate limit exceeded", {
          ip,
          limit,
          remaining,
          resetAt: new Date(reset).toISOString(),
          endpoint: request.url,
        });

        return NextResponse.json(
          { error: "Too many requests", retryAfter: reset },
          {
            status: 429,
            headers: {
              "X-RateLimit-Limit": limit.toString(),
              "X-RateLimit-Remaining": remaining.toString(),
              "X-RateLimit-Reset": reset.toString(),
              "Retry-After": Math.ceil((reset - Date.now()) / 1000).toString(),
            },
          },
        );
      }
    }

    // ── Parse form body ───────────────────────────────────
    const rawBody = await request.text();
    const params = new URLSearchParams(rawBody);

    // ── Twilio signature ──────────────────────────────────
    const signature = request.headers.get("x-twilio-signature");
    if (!signature) {
      logger.warn("RE Twilio webhook missing signature header");
      return new Response("", { status: 200 });
    }

    // ── Resolve organization from To number ───────────────
    const toRaw = params.get("To") || "";
    const toNumber = toRaw.replace(/^whatsapp:/, "");

    const org = await twilioWhatsAppService.getOrgByTwilioNumber(toNumber);
    if (!org) {
      logger.warn("RE Twilio webhook: unknown To number", { toNumber });
      return new Response("", { status: 200 });
    }

    // ── Resolve display name from owner's companyName ────
    let displayName = org.name;
    try {
      const { convexQuery: cq } = await import("@/lib/convex-client");
      const ownerUser = await cq<{ companyName?: string } | null>(
        "auth/queries:getUser",
        { id: org.ownerId },
      );
      if (ownerUser?.companyName) displayName = ownerUser.companyName;
    } catch {
      // non-blocking — fall back to org.name
    }

    // ── Get credentials & validate signature ──────────────
    const credentials = await twilioWhatsAppService.getCredentialsForOrg(
      org.id,
    );
    if (!credentials) {
      logger.error("RE Twilio webhook: credentials not found", {
        organizationId: org.id,
      });
      return new Response("", { status: 200 });
    }

    const forwardedHost = request.headers.get("x-forwarded-host");
    const forwardedProto = request.headers.get("x-forwarded-proto") || "https";
    const ngrokUrl = process.env.NGROK_URL?.replace(/\/+$/, "");
    const fullUrl = forwardedHost
      ? `${forwardedProto}://${forwardedHost}/api/webhooks/twilio-whatsapp-re`
      : ngrokUrl
        ? `${ngrokUrl}/api/webhooks/twilio-whatsapp-re`
        : request.url;

    const isValid = twilioWhatsAppService.validateSignature(
      credentials.authToken,
      signature,
      fullUrl,
      Object.fromEntries(params),
    );

    if (!isValid) {
      logger.warn("RE Twilio webhook: signature validation failed", {
        organizationId: org.id,
        validationUrl: fullUrl,
      });
      return new Response("", { status: 200 });
    }

    // ── Parse & sanitize message ──────────────────────────
    const message = twilioWhatsAppService.parseIncomingMessage(params);
    const body = sanitizeChatMessage(message.body);
    const phone = sanitizePhoneNumber(message.from.replace(/^whatsapp:/, ""));

    if (!phone) {
      logger.warn("RE Twilio webhook: empty phone after sanitization", {
        organizationId: org.id,
      });
      return new Response("", { status: 200 });
    }

    if (!body) {
      logger.debug("RE Twilio webhook: empty message body after sanitization", {
        organizationId: org.id,
        from: phone,
      });
      return new Response("", { status: 200 });
    }

    let language = detectLanguage(body);

    // ── Upsert conversation ───────────────────────────────
    let conversation = await prisma.org_whatsapp_conversations.findFirst({
      where: { organizationId: org.id, userPhone: phone },
    });

    const isNewConversation = !conversation;

    if (!conversation) {
      conversation = await prisma.org_whatsapp_conversations.create({
        data: {
          organizationId: org.id,
          userPhone: phone,
          userName: sanitizeChatMessage(message.profileName || "") || null,
          language,
          status: "active",
          lastMessageAt: new Date(),
          messageCount: 0,
          updatedAt: new Date(),
        },
      });
    }

    // ── Override language with stored preference ───────────
    let storedLanguagePreference: string | null = null;
    try {
      storedLanguagePreference = await getLanguagePreference(org.id, phone);
      if (
        storedLanguagePreference === "en" ||
        storedLanguagePreference === "ar"
      ) {
        language = storedLanguagePreference;
      }
    } catch (err) {
      logger.warn(
        "Failed to fetch language preference, falling back to detection",
        {
          organizationId: org.id,
          error: err instanceof Error ? err.message : "Unknown error",
        },
      );
    }

    // ── Bypass selecting_language if stored preference exists ──
    const conversationStateRaw =
      (conversation.conversationState as BookingFlowState | null) ?? null;
    let bypassedLanguageSelection = false;

    if (
      storedLanguagePreference &&
      conversationStateRaw?.phase === "selecting_language"
    ) {
      await prisma.org_whatsapp_conversations.update({
        where: { id: conversation.id },
        data: {
          conversationState: JSON.parse(JSON.stringify({ phase: "idle" })),
          language,
          updatedAt: new Date(),
        },
      });
      bypassedLanguageSelection = true;
    }

    // ── First-message bilingual greeting ──────────────────
    if (isNewConversation && !storedLanguagePreference) {
      await prisma.org_whatsapp_messages.create({
        data: {
          conversationId: conversation.id,
          role: "user",
          content: body,
          language,
          timestamp: new Date(),
        },
      });

      const greeting = await buildGreetingForOrg(org.id, displayName);

      if (!greeting.text) {
        // Owner opted out of automatic greetings — skip and transition to idle
        await prisma.org_whatsapp_conversations.update({
          where: { id: conversation.id },
          data: {
            conversationState: JSON.parse(JSON.stringify({ phase: "idle" })),
            lastMessageAt: new Date(),
            messageCount: { increment: 1 },
            updatedAt: new Date(),
          },
        });

        logger.info("RE Twilio WhatsApp: greeting opted out, skipping", {
          organizationId: org.id,
          conversationId: conversation.id,
        });

        return new Response("", { status: 200 });
      }

      await twilioWhatsAppService.sendTextMessage(
        credentials,
        message.from,
        greeting.text,
      );

      await prisma.org_whatsapp_messages.create({
        data: {
          conversationId: conversation.id,
          role: "assistant",
          content: greeting.text,
          language: "ar",
          timestamp: new Date(),
        },
      });

      await prisma.org_whatsapp_conversations.update({
        where: { id: conversation.id },
        data: {
          conversationState: JSON.parse(
            JSON.stringify({
              phase: greeting.requiresLanguageSelection
                ? "selecting_language"
                : "idle",
            }),
          ),
          lastMessageAt: new Date(),
          messageCount: { increment: 1 },
          updatedAt: new Date(),
        },
      });

      logger.info("RE Twilio WhatsApp: sent greeting to new user", {
        organizationId: org.id,
        conversationId: conversation.id,
        requiresLanguageSelection: greeting.requiresLanguageSelection,
      });

      return new Response("", { status: 200 });
    }

    // ── Language selection handler ──────────────────────────
    const conversationState = conversationStateRaw;

    if (
      !bypassedLanguageSelection &&
      conversationState?.phase === "selecting_language"
    ) {
      await prisma.org_whatsapp_messages.create({
        data: {
          conversationId: conversation.id,
          role: "user",
          content: body,
          language,
          timestamp: new Date(),
        },
      });

      const normalized = body.toLowerCase().trim();
      const arabicChars = (
        body.match(new RegExp(ARABIC_RANGE.source, "g")) || []
      ).length;
      const isArabicChoice =
        normalized === "1" ||
        normalized === "arabic" ||
        normalized === "عربي" ||
        normalized === "عربية" ||
        arabicChars > body.length * 0.5;
      const isEnglishChoice =
        normalized === "2" || normalized === "english" || normalized === "eng";

      if (!isArabicChoice && !isEnglishChoice) {
        const reprompt =
          `لو سمحت اختر رقم 1 أو 2 👇\n` +
          `Please reply with 1 or 2 👇\n\n` +
          `1️⃣ العربية (Arabic)\n` +
          `2️⃣ English`;

        await twilioWhatsAppService.sendTextMessage(
          credentials,
          message.from,
          reprompt,
        );

        await prisma.org_whatsapp_messages.create({
          data: {
            conversationId: conversation.id,
            role: "assistant",
            content: reprompt,
            language: "ar",
            timestamp: new Date(),
          },
        });

        await prisma.org_whatsapp_conversations.update({
          where: { id: conversation.id },
          data: {
            lastMessageAt: new Date(),
            messageCount: { increment: 1 },
            updatedAt: new Date(),
          },
        });

        return new Response("", { status: 200 });
      }

      const selectedLanguage: "en" | "ar" = isArabicChoice ? "ar" : "en";

      try {
        await setLanguagePreference(org.id, phone, selectedLanguage);
      } catch (err) {
        logger.error("Failed to save language preference", {
          organizationId: org.id,
          error: err instanceof Error ? err.message : "Unknown error",
        });
      }

      await prisma.org_whatsapp_conversations.update({
        where: { id: conversation.id },
        data: {
          language: selectedLanguage,
          conversationState: JSON.parse(JSON.stringify({ phase: "idle" })),
          lastMessageAt: new Date(),
          messageCount: { increment: 1 },
          updatedAt: new Date(),
        },
      });

      const confirmationText =
        selectedLanguage === "ar"
          ? "ممتاز! بنتواصل معاك بالعربي. شلون نقدر نساعدك اليوم؟ تبي تبحث عن عقار أو تحجز معاينة؟"
          : "Great! We'll communicate in English. How can we help you today? Would you like to search for a property or book a viewing?";

      await twilioWhatsAppService.sendTextMessage(
        credentials,
        message.from,
        confirmationText,
      );

      await prisma.org_whatsapp_messages.create({
        data: {
          conversationId: conversation.id,
          role: "assistant",
          content: confirmationText,
          language: selectedLanguage,
          timestamp: new Date(),
        },
      });

      logger.info("RE Twilio WhatsApp: language selected", {
        organizationId: org.id,
        conversationId: conversation.id,
        language: selectedLanguage,
      });

      return new Response("", { status: 200 });
    }

    // ── Human handoff check ─────────────────────────────────
    if (conversation.humanControlled) {
      const thirtyMinAgo = new Date(Date.now() - 30 * 60 * 1000);
      const stillActive =
        conversation.humanControlledAt &&
        new Date(conversation.humanControlledAt) > thirtyMinAgo;

      if (stillActive) {
        await prisma.org_whatsapp_messages.create({
          data: {
            conversationId: conversation.id,
            role: "user",
            content: body,
            language,
            timestamp: new Date(),
          },
        });

        await prisma.org_whatsapp_conversations.update({
          where: { id: conversation.id },
          data: {
            lastMessageAt: new Date(),
            messageCount: { increment: 1 },
            updatedAt: new Date(),
          },
        });

        logger.info("RE Twilio WhatsApp: human-controlled, skipping AI", {
          organizationId: org.id,
          conversationId: conversation.id,
        });

        return new Response("", { status: 200 });
      } else {
        await prisma.org_whatsapp_conversations.update({
          where: { id: conversation.id },
          data: {
            humanControlled: false,
            humanControlledAt: null,
            humanControlledBy: null,
            updatedAt: new Date(),
          },
        });
      }
    }

    // ── Load message history (last 20) ────────────────────
    const recentMessages = await prisma.org_whatsapp_messages.findMany({
      where: { conversationId: conversation.id },
      orderBy: { timestamp: "desc" },
      take: 20,
    });

    // ── Store inbound message ─────────────────────────────
    await prisma.org_whatsapp_messages.create({
      data: {
        conversationId: conversation.id,
        role: "user",
        content: body,
        language,
        timestamp: new Date(),
      },
    });

    // ── Build conversation context ────────────────────────
    const conversationHistory = (recentMessages as Array<{
      role: string;
      content: string;
      timestamp: Date;
    }>)
      .reverse()
      .map((m) => ({
        role: m.role as "user" | "assistant",
        content: m.content,
        timestamp: m.timestamp,
      }));

    const context: ConversationContext = {
      organizationId: org.id,
      organizationName: displayName,
      customerPhone: phone,
      customerName:
        sanitizeChatMessage(message.profileName || "") ||
        conversation.userName ||
        undefined,
      customerEmail: conversation.customerEmail || undefined,
      language,
      conversationHistory,
      conversationState:
        (conversation.conversationState as BookingFlowState | null) ?? null,
      isReturningCustomer: conversation.messageCount > 0,
    };

    // ── Process via Real Estate AI engine ──────────────────
    const result = await realEstateAIEngine.processMessage(context, body);

    // ── Guard against empty AI response ─────────────────
    if (!result.responseText) {
      result.responseText =
        language === "ar"
          ? "أنا هنا لمساعدتك بالعقارات. شلون أقدر أساعدك؟"
          : "I'm here to help with real estate. How can I assist you?";
    }

    // ── Qatari dialect post-processing ──────────────────
    if (language === "ar") {
      try {
        // Extract URL-containing lines before translation so the Qatari
        // dialect LLM doesn't strip or rewrite them
        const urlLineRegex = /^.*https?:\/\/\S+.*$/gm;
        const linkLines = result.responseText.match(urlLineRegex) || [];
        const textForTranslation =
          linkLines.length > 0
            ? result.responseText
                .replace(urlLineRegex, "")
                .replace(/\n{3,}/g, "\n\n")
                .trim()
            : result.responseText;

        const qatariResult =
          await qatariDialectService.translateToQatari(textForTranslation);
        if (qatariResult.translation) {
          result.responseText =
            linkLines.length > 0
              ? qatariResult.translation + "\n\n" + linkLines.join("\n")
              : qatariResult.translation;
        }
      } catch (err) {
        logger.warn("Qatari dialect translation failed, using original", {
          organizationId: org.id,
          error: err instanceof Error ? err.message : "Unknown error",
        });
      }
    }

    // ── Store outbound message ────────────────────────────
    await prisma.org_whatsapp_messages.create({
      data: {
        conversationId: conversation.id,
        role: "assistant",
        content: result.responseText,
        language,
        intent: result.intent,
        timestamp: new Date(),
      },
    });

    // ── Update conversation state ─────────────────────────
    await prisma.org_whatsapp_conversations.update({
      where: { id: conversation.id },
      data: {
        lastMessageAt: new Date(),
        messageCount: { increment: 1 },
        ...(result.newState
          ? { conversationState: JSON.parse(JSON.stringify(result.newState)) }
          : {}),
        updatedAt: new Date(),
      },
    });

    // ── Send response via Twilio ──────────────────────────
    const sendResult = await twilioWhatsAppService.sendTextMessage(
      credentials,
      message.from,
      result.responseText,
    );

    if (!sendResult.success) {
      logger.error("Failed to send RE Twilio WhatsApp response", {
        organizationId: org.id,
        error: sendResult.error,
      });
    }

    logger.info("RE Twilio WhatsApp message processed", {
      organizationId: org.id,
      intent: result.intent,
      phone: phone.slice(-4),
      messageSid: message.messageSid,
    });

    // Twilio expects empty 200
    return new Response("", { status: 200 });
  } catch (error) {
    logger.error("RE Twilio WhatsApp webhook error", { error });
    return new Response("", { status: 200 });
  }
}
