import { Controller, Get, Post, Body, Query, HttpCode, HttpStatus, Res, Logger, UseGuards, Request, BadRequestException } from '@nestjs/common';
import type { Response } from 'express';
import { WhatsAppBotEngine } from './services/whatsapp-bot.engine';
import { WhatsAppService } from './services/whatsapp.service';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { SubscriptionGuard } from '../subscription/subscription.guard';
import { PrismaService } from '../../prisma/prisma.service';
import { EncryptionService } from '../../common/services/encryption.service';

@Controller()

export class WhatsAppWebhookController {
  private readonly logger = new Logger(WhatsAppWebhookController.name);

  constructor(
    private readonly botEngine: WhatsAppBotEngine,
    private readonly whatsappService: WhatsAppService,
    private readonly prisma: PrismaService,
    private readonly encryptionService: EncryptionService,
  ) { }

  private cleanPhoneNumber(phone: string): string {
    let clean = (phone || '').trim().replace(/[+\s]+/g, '');
    if (/^0[0-9]{9}$/.test(clean)) {
      clean = '263' + clean.substring(1);
    }
    return clean;
  }

  // 1. Meta WhatsApp Webhook Validation (GET)
  @Get('webhooks/whatsapp')
  verifyWebhook(
    @Query('hub.mode') mode: string,
    @Query('hub.verify_token') token: string,
    @Query('hub.challenge') challenge: string,
    @Res() res: Response,
  ) {
    const verifyToken = process.env.WHATSAPP_VERIFY_TOKEN || 'whatsapp-verify-token';

    if (mode === 'subscribe' && token === verifyToken) {
      this.logger.log('WhatsApp Webhook validated successfully!');
      return res.status(HttpStatus.OK).send(challenge);
    }
    this.logger.warn('WhatsApp Webhook validation failed.');
    return res.status(HttpStatus.FORBIDDEN).send('Forbidden');
  }

  // 2. Meta WhatsApp Webhook Receiver (POST)
  @Post('webhooks/whatsapp')
  @HttpCode(HttpStatus.OK)
  async handleIncomingWebhook(@Body() payload: any, @Request() req: any) {
    try {
      this.logger.debug('Received WhatsApp webhook payload:', JSON.stringify(payload));
      
      const entry = payload.entry?.[0];
      const changes = entry?.changes?.[0];
      const value = changes?.value;

      // Handle message status updates (delivered, read, failed, etc.)
      const statuses = value?.statuses?.[0];
      if (statuses) {
        const wamid = statuses.id;
        const status = statuses.status?.toUpperCase(); // DELIVERED, READ, FAILED, etc.
        const recipientId = statuses.recipient_id ? this.cleanPhoneNumber(statuses.recipient_id) : undefined;

        this.logger.debug(`Received WhatsApp status update: id=${wamid}, status=${status}, recipient=${recipientId}`);

        const messageLog = await this.prisma.messageLog.findFirst({
          where: {
            whatsappNumber: recipientId,
            rawPayloadJson: {
              contains: wamid,
            },
          },
        });

        if (messageLog) {
          await this.prisma.messageLog.update({
            where: { id: messageLog.id },
            data: {
              status: status,
            },
          });
          this.logger.debug(`Updated MessageLog status to ${status} for message ${wamid}`);
        }
        return { success: true };
      }

      const message = value?.messages?.[0];
 
      if (message) {
        const from = message.from.replace(/^\+/, '');
        let textBody: string | undefined;
        let isOrderMessage = false;
        let orderItems: any[] = [];
 
        // Parse text body depending on Meta's message type
        if (message.type === 'text') {
          textBody = message.text?.body;
        } else if (message.type === 'interactive') {
          const interactive = message.interactive;
          if (interactive?.type === 'list_reply') {
            const rawId = interactive.list_reply?.id || '';
            textBody = rawId.includes('_') ? rawId.split('_')[0] : rawId;
          } else if (interactive?.type === 'button_reply') {
            const rawId = interactive.button_reply?.id || '';
            textBody = rawId.includes('_') ? rawId.split('_')[0] : rawId;
          } else if (interactive?.type === 'nfm_reply') {
            try {
              const flowData = JSON.parse(interactive.nfm_reply?.response_json || '{}');
              if (flowData.quantity) {
                textBody = String(flowData.quantity);
              } else if (flowData.value) {
                textBody = String(flowData.value);
              } else {
                textBody = 'flow_submitted';
              }
            } catch {
              textBody = 'flow_submitted';
            }
          }
        } else if (message.type === 'button') {
          textBody = message.button?.payload;
        } else if (message.type === 'order') {
          isOrderMessage = true;
          orderItems = message.order?.product_items || [];
          textBody = message.order?.text || 'Sent cart from WhatsApp Catalog';
        } else if (message.type === 'location') {
          const loc = message.location;
          textBody = `LOCATION_SHARED:lat=${loc.latitude},lng=${loc.longitude},name=${loc.name || ''},address=${loc.address || ''}`;
        }
 
        if (from && (textBody || isOrderMessage)) {
          // Dynamic WABA Phone Number ID routing
          const recipientPhoneId = value?.metadata?.phone_number_id;
          let businessId: string | undefined;
 
          if (recipientPhoneId) {
            const integrations = await this.prisma.businessIntegration.findMany({
              where: { integrationType: 'whatsapp', status: 'ACTIVE' },
            });
 
            for (const integration of integrations) {
              try {
                const creds = this.encryptionService.decryptJson<{ phoneNumberId?: string }>(
                  integration.credentialsEncrypted,
                );
                if (creds?.phoneNumberId === recipientPhoneId) {
                  businessId = integration.businessId;
                  break;
                }
              } catch {
                // ignore decryption errors for malformed configs
              }
            }
          }
 
          // Fallback to the first business for developer sandboxes / mock setups
          if (!businessId) {
            const firstBusiness = await this.prisma.business.findFirst();
            businessId = firstBusiness?.id;
          }
 
          if (businessId) {
            // Find active session to determine correct business mapping
            const session = await this.prisma.session.findFirst({
              where: { whatsappNumber: from },
              include: { business: true },
            });
            const isExpired = session ? session.expiresAt < new Date() : true;
            const isBizActive = session?.business?.status === 'ACTIVE';
            const activeBusinessId = (session && !isExpired && isBizActive) ? session.businessId : businessId;
 
            if (isOrderMessage) {
              // Handle Native Catalog & Cart Submissions
              try {
                // Fetch or create customer
                let customer = await this.prisma.customer.findFirst({
                  where: { businessId: activeBusinessId, whatsappNumber: from },
                });
                if (!customer) {
                  customer = await this.prisma.customer.create({
                    data: {
                      businessId: activeBusinessId,
                      whatsappNumber: from,
                      name: 'WhatsApp Customer',
                    },
                  });
                }

                // Get or initialize active session
                const activeSession = await this.botEngine['sessionService'].getOrCreateSession(
                  activeBusinessId,
                  from,
                  customer.id,
                );

                let addedCount = 0;
                let itemListText = '';

                for (const item of orderItems) {
                  const localProduct = await this.prisma.product.findFirst({
                    where: { businessId: activeBusinessId, externalProductId: item.product_retailer_id },
                    include: { variants: true },
                  });

                  if (localProduct) {
                    const qty = parseInt(item.quantity) || 1;
                    const variantId = localProduct.variants?.[0]?.id || undefined;
                    await this.botEngine['cartsService'].addToCart(
                      activeBusinessId,
                      from,
                      localProduct.id,
                      qty,
                      variantId,
                    );
                    addedCount++;
                    itemListText += `- *${localProduct.name}* (Qty: ${qty})\n`;
                  }
                }

                if (addedCount > 0) {
                  // Update session step to POST_ADD_TO_CART
                  await this.botEngine['sessionService'].updateStep(activeSession.id, 'POST_ADD_TO_CART');

                  const responseText = `Received your cart from WhatsApp Catalog!\n\n` +
                    `*Items added:*\n${itemListText}\n` +
                    `What would you like to do next?\n` +
                    `1. View Cart\n` +
                    `2. Continue Shopping`;

                  await this.whatsappService.logIncomingMessage(activeBusinessId, from, `[Catalog Order]: ${itemListText.trim()}`, payload);

                  await this.whatsappService.sendMessage(activeBusinessId, from, responseText, {
                    type: 'pills',
                    options: [
                      { id: '1', title: 'View Cart' },
                      { id: '2', title: 'Continue Shopping' },
                    ]
                  });
                } else {
                  // Fallback if no valid products mapped
                  const responseText = `We received your catalog cart, but we couldn't match the items to our store records. Please try browsing our catalog directly via the chatbot menus.`;
                  await this.whatsappService.sendMessage(activeBusinessId, from, responseText);
                }
              } catch (err: any) {
                this.logger.error(`Error processing native catalog cart submission: ${err.message}`);
                const responseText = `An error occurred while processing your catalog cart. Please try again or chat with our support.`;
                await this.whatsappService.sendMessage(activeBusinessId, from, responseText);
              }
            } else {
              // Standard message handling
              await this.whatsappService.logIncomingMessage(activeBusinessId, from, textBody || '', payload);
              const response = await this.botEngine.processMessage(activeBusinessId, from, textBody || '');
              
              const enrichedPayload = {
                ...(response.payload || {}),
                publicHost: req.headers.host,
                credentialsBusinessId: businessId,
              };
   
              // Fetch updated session to see if businessId changed (e.g. at SELECT_STORE step)
              const updatedSession = await this.prisma.session.findFirst({
                where: { whatsappNumber: from },
              });
              const finalBusinessId = updatedSession ? updatedSession.businessId : activeBusinessId;
                
              await this.whatsappService.sendMessage(finalBusinessId, from, response.text, enrichedPayload);
            }
          }
        }
      }
      return { success: true };
    } catch (err: any) {
      this.logger.error(`Error processing WhatsApp webhook: ${err.message}`);
      return { success: false, error: err.message };
    }
  }

  // 3. WhatsApp Chat Simulator (POST)
  // Used by the Next.js Dashboard to simulate an incoming WhatsApp message from a customer
  @Post('webhooks/whatsapp/simulate')
  @HttpCode(HttpStatus.OK)
  async simulateIncomingMessage(
    @Body() body: { businessId: string; whatsappNumber: string; message: string },
  ) {
    const { businessId, whatsappNumber, message } = body;
    const cleanPhone = this.cleanPhoneNumber(whatsappNumber);

    // Determine the active business using the session first
    const session = await this.botEngine['prisma'].session.findFirst({
      where: { whatsappNumber: cleanPhone },
      include: { business: true },
    });
    const isExpired = session ? session.expiresAt < new Date() : true;
    const isBizActive = session?.business?.status === 'ACTIVE';
    const activeBusinessId = (session && !isExpired && isBizActive) ? session.businessId : businessId;

    // Log the user's message
    await this.whatsappService.logIncomingMessage(activeBusinessId, cleanPhone, message, { simulated: true });

    // Process using Bot State Machine
    const response = await this.botEngine.processMessage(activeBusinessId, cleanPhone, message);

    // Get the final businessId after processing (e.g. in case they switched stores)
    const updatedSession = await this.botEngine['prisma'].session.findFirst({
      where: { whatsappNumber: cleanPhone },
    });
    const finalBusinessId = updatedSession ? updatedSession.businessId : activeBusinessId;

    // Log the bot's reply
    await this.whatsappService.sendMessage(finalBusinessId, cleanPhone, response.text, {
      simulated: true,
      ...response.payload
    });

    // Fetch active session to return to the UI side-panel
    const finalSession = await this.botEngine['prisma'].session.findFirst({
      where: { whatsappNumber: cleanPhone },
    });

    let sessionState = {};
    if (finalSession) {
      try {
        sessionState = {
          currentStep: finalSession.currentStep,
          data: JSON.parse(finalSession.sessionDataJson),
          expiresAt: finalSession.expiresAt,
        };
      } catch {
        sessionState = {};
      }
    }

    return {
      success: true,
      reply: response.text,
      sessionState,
    };
  }

  // 3.5. Webview Confirmation Receiver (POST)
  @Post('webhooks/whatsapp/webview-confirm')
  @HttpCode(HttpStatus.OK)
  async webviewConfirm(
    @Body() body: { businessId: string; whatsappNumber: string; productId: string; quantity: number },
  ) {
    try {
      const { businessId, whatsappNumber, productId, quantity } = body;
      const cleanPhone = this.cleanPhoneNumber(whatsappNumber);

      const session = await this.prisma.session.findFirst({
        where: { whatsappNumber: cleanPhone },
      });
      if (!session) {
        return { success: false, error: 'Session not found' };
      }

      const product = await this.prisma.product.findUnique({
        where: { id: productId },
        include: { variants: true },
      });
      if (!product) {
        return { success: false, error: 'Product not found' };
      }

      const variantId = product.variants?.[0]?.id || undefined;
      await this.botEngine['cartsService'].addToCart(businessId, cleanPhone, productId, quantity, variantId);

      const cart = await this.botEngine['cartsService'].getOrCreateCart(businessId, cleanPhone);
      const cartItem = cart.items.find(item => item.productId === productId);
      const totalQty = cartItem ? cartItem.quantity : quantity;

      await this.botEngine['sessionService'].updateStep(session.id, 'POST_ADD_TO_CART');

      const responseText = `Added *${quantity}* more *${product.name}* to your cart! (Total in cart: *${totalQty}*)\n\n` +
        `What would you like to do next?\n` +
        `1. View Cart\n` +
        `2. Continue Shopping\n` +
        `3. Add More`;

      await this.whatsappService.sendMessage(businessId, cleanPhone, responseText, {
        type: 'pills',
        options: [
          { id: '1', title: 'View Cart' },
          { id: '2', title: 'Continue Shopping' },
          { id: '3', title: 'Add More' }
        ]
      });

      return { success: true, totalQuantity: totalQty };
    } catch (err: any) {
      this.logger.error(`Error in webview confirmation: ${err.message}`);
      return { success: false, error: err.message };
    }
  }

  // 4. API Endpoint for Dashboard to view Chat Message Logs
  @Get('api/whatsapp/logs')
  @UseGuards(JwtAuthGuard, SubscriptionGuard)
  async getMessageLogs(@Request() req: any, @Query('phone') phone?: string) {
    const cleanPhone = phone ? this.cleanPhoneNumber(phone) : undefined;
    return this.whatsappService.listMessageLogs(req.user.id, cleanPhone);
  }

  // 5. API Endpoint for Dashboard to get a list of active sessions
  @Get('api/whatsapp/sessions')
  @UseGuards(JwtAuthGuard, SubscriptionGuard)
  async getActiveSessions(@Request() req: any) {
    return this.botEngine['prisma'].session.findMany({
      where: { businessId: req.user.id },
      include: {
        customer: true,
      },
      orderBy: { updatedAt: 'desc' },
    });
  }

  // 6. API Endpoint for Dashboard to reset a customer's session
  @Post('api/whatsapp/sessions/reset')
  @UseGuards(JwtAuthGuard, SubscriptionGuard)
  @HttpCode(HttpStatus.OK)
  async resetSession(@Request() req: any, @Body() body: { whatsappNumber: string }) {
    const cleanPhone = this.cleanPhoneNumber(body.whatsappNumber);
    const session = await this.botEngine['prisma'].session.findFirst({
      where: { whatsappNumber: cleanPhone },
    });
    if (session) {
      await this.botEngine['sessionService'].clearSession(session.id);
    }
    return { success: true };
  }

  // 7. API Endpoint for Dashboard to simulate sending an interactive template
  @Post('api/whatsapp/templates/simulate')
  @UseGuards(JwtAuthGuard, SubscriptionGuard)
  @HttpCode(HttpStatus.OK)
  async simulateTemplate(
    @Request() req: any,
    @Body() body: { whatsappNumber: string; templateType: string; bodyText: string; payload: any },
  ) {
    const businessId = req.user.id;
    const { whatsappNumber, templateType, bodyText, payload } = body;
    const cleanPhone = this.cleanPhoneNumber(whatsappNumber);

    // Log the outbound template message using whatsappService
    await this.whatsappService.sendMessage(businessId, cleanPhone, bodyText, payload);

    return { success: true };
  }

  // 8. API Endpoint for Dashboard to broadcast a marketing message or notice to all customers
  @Post('api/whatsapp/broadcast')
  @UseGuards(JwtAuthGuard, SubscriptionGuard)
  @HttpCode(HttpStatus.OK)
  async broadcastMessage(
    @Request() req: any,
    @Body() body: { message: string; recipientPhone?: string },
  ) {
    const businessId = req.user.id;
    const { message, recipientPhone } = body;

    if (!message || message.trim() === '') {
      throw new BadRequestException('Message body cannot be empty');
    }

    if (recipientPhone && recipientPhone.trim() !== '') {
      // Send to single customer
      const cleanPhone = this.cleanPhoneNumber(recipientPhone);
      try {
        await this.whatsappService.sendMessage(businessId, cleanPhone, message, { type: 'text' });
        return {
          success: true,
          count: 1,
          failed: 0,
          message: `Successfully sent message to +${cleanPhone}.`,
        };
      } catch (err: any) {
        this.logger.error(`Failed to send message to ${cleanPhone}: ${err.message}`);
        return {
          success: false,
          count: 0,
          failed: 1,
          message: `Failed to send message to +${cleanPhone}: ${err.message}`,
        };
      }
    }

    // Find all customers who have interacted with this business
    const customers = await this.prisma.customer.findMany({
      where: { businessId },
      select: { whatsappNumber: true },
    });

    if (customers.length === 0) {
      return { success: true, count: 0, message: 'No customers found to broadcast to.' };
    }

    let successCount = 0;
    let failCount = 0;

    for (const customer of customers) {
      try {
        const cleanPhone = this.cleanPhoneNumber(customer.whatsappNumber);
        await this.whatsappService.sendMessage(businessId, cleanPhone, message, { type: 'text' });
        successCount++;
      } catch (err: any) {
        this.logger.error(`Failed to send broadcast message to ${customer.whatsappNumber}: ${err.message}`);
        failCount++;
      }
    }

    return {
      success: true,
      count: successCount,
      failed: failCount,
      message: `Successfully broadcasted to ${successCount} customers (${failCount} failed).`,
    };
  }
}
