import { Injectable, Logger } from '@nestjs/common';
import { PrismaService } from '../../../prisma/prisma.service';
import { EncryptionService } from '../../../common/services/encryption.service';
import axios from 'axios';
import * as fs from 'fs';
import * as path from 'path';

@Injectable()
export class WhatsAppService {
  private readonly logger = new Logger(WhatsAppService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly encryptionService: EncryptionService,
  ) {}

  async sendMessage(
    businessId: string,
    toPhone: string,
    body: string,
    rawPayload?: any,
  ): Promise<boolean> {
    const cleanPhone = toPhone.replace(/^\+/, '').trim();
    this.logger.log(`Sending WhatsApp message to ${cleanPhone}: "${body.substring(0, 40)}..."`);

    // 1. Create message log in local database
    const logEntry = await this.prisma.messageLog.create({
      data: {
        businessId,
        whatsappNumber: cleanPhone,
        direction: 'outbound',
        messageType: rawPayload?.type || 'text',
        messageBody: body,
        status: 'SENT',
        rawPayloadJson: rawPayload ? JSON.stringify(rawPayload) : null,
      },
    });

    // If it's a dashboard simulation, don't send to a real WhatsApp number
    if (rawPayload?.simulated) {
      this.logger.debug(`Sandbox dashboard simulation message. Skipping real API dispatch.`);
      return true;
    }

    // 2. Attempt to send via WhatsApp Cloud API if credentials are configured
    let credsBusinessId = rawPayload?.credentialsBusinessId || businessId;

    if (!rawPayload?.credentialsBusinessId) {
      try {
        const latestInbound = await this.prisma.messageLog.findFirst({
          where: { whatsappNumber: cleanPhone, direction: 'inbound' },
          orderBy: { createdAt: 'desc' },
        });
        if (latestInbound && latestInbound.rawPayloadJson) {
          const raw = JSON.parse(latestInbound.rawPayloadJson);
          const phoneId = raw?.entry?.[0]?.changes?.[0]?.value?.metadata?.phone_number_id;
          if (phoneId) {
            const integrations = await this.prisma.businessIntegration.findMany({
              where: { integrationType: 'whatsapp', status: 'ACTIVE' },
            });
            for (const integ of integrations) {
              const creds = this.encryptionService.decryptJson<{ phoneNumberId?: string }>(
                integ.credentialsEncrypted,
              );
              if (creds?.phoneNumberId === phoneId) {
                credsBusinessId = integ.businessId;
                this.logger.log(`Resolved WABA credentials using latest inbound message metadata: phoneId=${phoneId}, businessId=${credsBusinessId}`);
                break;
              }
            }
          }
        }
      } catch (err: any) {
        this.logger.error(`Error resolving WABA credentials from latest inbound message: ${err.message}`);
      }
    }

    let integration = await this.prisma.businessIntegration.findFirst({
      where: { businessId: credsBusinessId, integrationType: 'whatsapp', status: 'ACTIVE' },
    });

    if (!integration) {
      // Fallback: use any active WABA integration in the database (e.g. host WABA)
      integration = await this.prisma.businessIntegration.findFirst({
        where: { integrationType: 'whatsapp', status: 'ACTIVE' },
      });
    }

    if (!integration) {
      this.logger.debug(`No WhatsApp WABA credentials configured. Message stored in sandbox logs.`);
      return true;
    }

    try {
      const creds = this.encryptionService.decryptJson<{ phoneNumberId?: string; accessToken?: string }>(
        integration.credentialsEncrypted,
      );

      if (!creds || !creds.phoneNumberId || !creds.accessToken) {
        this.logger.warn(`WhatsApp WABA credentials for business ${businessId} are invalid or incomplete.`);
        try {
          await this.prisma.messageLog.update({
            where: { id: logEntry.id },
            data: { status: 'FAILED' },
          });
        } catch (logErr) {
          this.logger.error(`Failed to update message log status: ${logErr.message}`);
        }
        return false;
      }

      const { phoneNumberId, accessToken } = creds;
      const url = `https://graph.facebook.com/v19.0/${phoneNumberId}/messages`;

      let metaPayload: any = {
        messaging_product: 'whatsapp',
        recipient_type: 'individual',
        to: cleanPhone,
      };

      if (rawPayload && (rawPayload.interactive || rawPayload.type === 'interactive')) {
        // Direct Meta interactive Flow / Template payload pass-through
        metaPayload = {
          ...metaPayload,
          ...rawPayload,
          to: cleanPhone
        };
      } else if (rawPayload && rawPayload.type === 'button_grid' && Array.isArray(rawPayload.buttons)) {
        const buttons = rawPayload.buttons;
        if (buttons.length <= 3) {
          metaPayload.type = 'interactive';
          metaPayload.interactive = {
            type: 'button',
            body: { text: body },
            action: {
              buttons: buttons.map((btn: any) => ({
                type: 'reply',
                reply: {
                  id: `${btn.id}_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`,
                  title: btn.title.substring(0, 20),
                },
              })),
            },
          };
        } else {
          metaPayload.type = 'interactive';
          metaPayload.interactive = {
            type: 'list',
            body: { text: body },
            action: {
              button: 'Select Option',
              sections: [
                {
                  title: 'Options',
                  rows: buttons.slice(0, 10).map((btn: any) => ({
                    id: btn.id,
                    title: btn.title.substring(0, 24),
                  })),
                },
              ],
            },
          };
        }
      } else if (rawPayload && rawPayload.type === 'pills' && Array.isArray(rawPayload.options)) {
        const options = rawPayload.options;
        if (options.length <= 3) {
          metaPayload.type = 'interactive';
          metaPayload.interactive = {
            type: 'button',
            body: { text: body },
            action: {
              buttons: options.map((opt: any) => ({
                type: 'reply',
                reply: {
                  id: `${opt.id}_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`,
                  title: opt.title.substring(0, 20),
                },
              })),
            },
          };
        } else {
          metaPayload.type = 'interactive';
          metaPayload.interactive = {
            type: 'list',
            body: { text: body },
            action: {
              button: rawPayload.buttonText || 'Select Option',
              sections: [
                {
                  title: 'Options',
                  rows: options.slice(0, 10).map((opt: any) => ({
                    id: opt.id,
                    title: opt.title.substring(0, 24),
                  })),
                },
              ],
            },
          };
        }
      } else if (rawPayload && rawPayload.type === 'product_carousel' && Array.isArray(rawPayload.products)) {
        const products = rawPayload.products;
        if (products.length < 3) {
          const buttons = products.map((prod: any) => ({
            type: 'reply',
            reply: {
              id: prod.id,
              title: prod.name.substring(0, 20),
            },
          }));
          buttons.push({
            type: 'reply',
            reply: {
              id: 'B',
              title: 'Back',
            },
          });
          
          metaPayload.type = 'interactive';
          metaPayload.interactive = {
            type: 'button',
            body: { text: body },
            action: {
              buttons,
            },
          };
        } else {
          const rows = products.slice(0, 9).map((prod: any) => ({
            id: prod.id,
            title: prod.name.substring(0, 24),
            description: prod.description ? prod.description.substring(0, 72) : `$${prod.price.toFixed(2)}`,
          }));
          
          rows.push({
            id: 'B',
            title: 'Back to Categories',
            description: 'Go back to select another category',
          });

          metaPayload.type = 'interactive';
          metaPayload.interactive = {
            type: 'list',
            body: { text: body },
            action: {
              button: 'Select Product',
              sections: [
                {
                  title: 'Products',
                  rows,
                },
              ],
            },
          };
        }
      } else if (rawPayload && rawPayload.type === 'cta_url' && rawPayload.url) {
        // WhatsApp Cloud API: interactive cta_url – renders as a tappable link button
        const buttonText = (rawPayload.urlButtonText || 'Open Link').substring(0, 20);
        const linkUrl: string = rawPayload.url;

        metaPayload.type = 'interactive';
        metaPayload.interactive = {
          type: 'cta_url',
          body: { text: body.substring(0, 1024) },
          action: {
            name: 'cta_url',
            parameters: {
              display_text: buttonText,
              url: linkUrl,
            },
          },
        };

        // If there are follow-up quick reply options, we schedule a second message
        if (Array.isArray(rawPayload.followUpOptions) && rawPayload.followUpOptions.length > 0) {
          // Send the CTA button first, then the follow-up quick reply as a second message
          this.logger.debug(`Sending Meta WABA request to ${url}: ${JSON.stringify(metaPayload)}`);
          const firstResp = await axios.post(url, metaPayload, {
            headers: {
              Authorization: `Bearer ${accessToken}`,
              'Content-Type': 'application/json',
            },
          });
          this.logger.log(`Meta WABA CTA button sent.`);

          // Store the wamid in rawPayloadJson
          const wamid = firstResp.data?.messages?.[0]?.id;
          if (wamid) {
            const enrichedPayload = {
              ...(rawPayload || {}),
              wamid,
            };
            await this.prisma.messageLog.update({
              where: { id: logEntry.id },
              data: {
                rawPayloadJson: JSON.stringify(enrichedPayload),
              },
            });
          }

          // Build follow-up quick-reply message
          const followUpOptions = rawPayload.followUpOptions;
          const followUpPayload: any = {
            messaging_product: 'whatsapp',
            recipient_type: 'individual',
            to: cleanPhone,
            type: 'interactive',
            interactive: {
              type: 'button',
              body: { text: 'What would you like to do next?' },
              action: {
                buttons: followUpOptions.slice(0, 3).map((opt: any) => ({
                  type: 'reply',
                  reply: { id: opt.id, title: opt.title.substring(0, 20) },
                })),
              },
            },
          };
          this.logger.debug(`Sending follow-up quick reply: ${JSON.stringify(followUpPayload)}`);
          const followUpResp = await axios.post(url, followUpPayload, {
            headers: {
              Authorization: `Bearer ${accessToken}`,
              'Content-Type': 'application/json',
            },
          });
          this.logger.log(`Follow-up message sent. Response: ${JSON.stringify(followUpResp.data)}`);
          return true;
        }
      } else if (rawPayload && rawPayload.type === 'product_detail') {
        const product = rawPayload.product;
        const localPath = product?.imageUrl ? this.getLocalFilePath(product.imageUrl) : null;
        
        let mediaId: string | null = null;
        const isWebp = localPath && (localPath.toLowerCase().endsWith('.webp') || localPath.toLowerCase().includes('.webp'));
        
        if (localPath && !isWebp) {
          const mimeType = this.getMimeType(localPath);
          mediaId = await this.uploadMediaToMeta(phoneNumberId, accessToken, localPath, mimeType);
        }
 
        let actionButtons = [
          { type: 'reply', reply: { id: '1', title: 'Add 1 to Cart' } },
          { type: 'reply', reply: { id: '2', title: 'Add 2 to Cart' } },
          { type: 'reply', reply: { id: 'B', title: 'Back' } }
        ];

        if (Array.isArray(rawPayload.buttons) && rawPayload.buttons.length > 0) {
          actionButtons = rawPayload.buttons.map((btn: any) => ({
            type: 'reply',
            reply: {
              id: btn.id,
              title: btn.title.substring(0, 20),
            }
          }));
        }
 
        if (mediaId) {
          metaPayload.type = 'interactive';
          metaPayload.interactive = {
            type: 'button',
            header: {
              type: 'image',
              image: {
                id: mediaId
              }
            },
            body: { text: body.substring(0, 1024) },
            action: {
              buttons: actionButtons
            }
          };
        } else if (product?.imageUrl) {
          const imageUrl = this.getPublicImageUrl(product.imageUrl, rawPayload.publicHost);
          metaPayload.type = 'interactive';
          metaPayload.interactive = {
            type: 'button',
            header: {
              type: 'image',
              image: {
                link: imageUrl
              }
            },
            body: { text: body.substring(0, 1024) },
            action: {
              buttons: actionButtons
            }
          };
        } else {
          metaPayload.type = 'interactive';
          metaPayload.interactive = {
            type: 'button',
            body: { text: body.substring(0, 1024) },
            action: {
              buttons: actionButtons
            }
          };
        }
      } else {
        metaPayload.type = 'text';
        metaPayload.text = { body };
      }
 
      this.logger.debug(`Sending Meta WABA request to ${url}: ${JSON.stringify(metaPayload)}`);
      
      const response = await axios.post(url, metaPayload, {
        headers: {
          Authorization: `Bearer ${accessToken}`,
          'Content-Type': 'application/json',
        },
      });
 
      // Update wamid in log
      const wamid = response.data?.messages?.[0]?.id;
      if (wamid) {
        const enrichedPayload = {
          ...(rawPayload || {}),
          wamid,
        };
        await this.prisma.messageLog.update({
          where: { id: logEntry.id },
          data: {
            rawPayloadJson: JSON.stringify(enrichedPayload),
          },
        });
      }

      this.logger.log(`Meta WABA message sent. Response: ${JSON.stringify(response.data)}`);
      return true;
    } catch (err: any) {
      const errorMsg = err.response?.data ? JSON.stringify(err.response.data) : err.message;
      this.logger.error(`Failed to send WhatsApp message via Meta Cloud API: ${errorMsg}`);
      try {
        await this.prisma.messageLog.update({
          where: { id: logEntry.id },
          data: { status: 'FAILED' },
        });
      } catch (logErr) {
        this.logger.error(`Failed to update message log status: ${logErr.message}`);
      }
      return false;
    }
 
    return true;
  }

  /**
   * Sends a plain text WhatsApp message from the polling service.
   * Does not interact with session or bot state.
   */
  async sendTextNotification(businessId: string, toPhone: string, message: string): Promise<void> {
    this.logger.log(`Sending text notification to ${toPhone}`);
    await this.sendMessage(businessId, toPhone, message, { type: 'text' });
  }

  private getPublicImageUrl(imageUrl: string, publicHost?: string): string {
    if (!imageUrl) return '';
 
    let resolvedUrl = imageUrl;

    // Check if it's already a full public URL (non-localhost)
    if (imageUrl.startsWith('http')) {
      if (imageUrl.includes('localhost:') || imageUrl.includes('127.0.0.1')) {
        // It's a localhost URL, we need to map it to the public domain
        const publicUrl = process.env.PUBLIC_URL || (publicHost ? `https://${publicHost}` : null);
        if (publicUrl) {
          try {
            const urlObj = new URL(imageUrl);
            const publicUrlObj = new URL(publicUrl.startsWith('http') ? publicUrl : `https://${publicUrl}`);
            urlObj.protocol = publicUrlObj.protocol;
            urlObj.host = publicUrlObj.host;
            resolvedUrl = urlObj.toString();
          } catch {
            const cleanPublic = publicUrl.replace(/\/$/, '');
            resolvedUrl = imageUrl.replace(/https?:\/\/localhost:\d+/, cleanPublic);
          }
        }
      }
    } else {
      // Relative path (e.g. /uploads/...)
      const publicUrl = process.env.PUBLIC_URL || (publicHost ? `https://${publicHost}` : 'http://localhost:3001');
      const cleanPublic = publicUrl.startsWith('http') ? publicUrl.replace(/\/$/, '') : `https://${publicUrl.replace(/\/$/, '')}`;
      const cleanImage = imageUrl.replace(/^\//, '');
      resolvedUrl = `${cleanPublic}/${cleanImage}`;
    }

    // Convert WebP images to JPG on the fly using weserv.nl proxy since WhatsApp doesn't support WebP links
    if (resolvedUrl.toLowerCase().includes('.webp')) {
      const urlWithoutProtocol = resolvedUrl.replace(/^https?:\/\//, '');
      return `https://images.weserv.nl/?url=${encodeURIComponent(urlWithoutProtocol)}&output=jpg`;
    }

    return resolvedUrl;
  }

  async logIncomingMessage(
    businessId: string,
    fromPhone: string,
    body: string,
    rawPayload?: any,
  ) {
    const cleanPhone = fromPhone.replace(/^\+/, '').trim();
    // Check if customer exists, if not create
    let customer = await this.prisma.customer.findFirst({
      where: { businessId, whatsappNumber: cleanPhone },
    });

    if (!customer) {
      customer = await this.prisma.customer.create({
        data: {
          businessId,
          whatsappNumber: cleanPhone,
          name: 'WhatsApp Customer',
        },
      });
    }

    return this.prisma.messageLog.create({
      data: {
        businessId,
        customerId: customer.id,
        whatsappNumber: cleanPhone,
        direction: 'inbound',
        messageType: 'text',
        messageBody: body,
        status: 'RECEIVED',
        rawPayloadJson: rawPayload ? JSON.stringify(rawPayload) : null,
      },
    });
  }

  async listMessageLogs(businessId: string, whatsappNumber?: string) {
    const where: any = { businessId };
    if (whatsappNumber) {
      const cleanPhone = whatsappNumber.replace(/^\+/, '').trim();
      where.whatsappNumber = cleanPhone;
    }
    return this.prisma.messageLog.findMany({
      where,
      orderBy: { createdAt: 'asc' },
      take: 100, // limit to 100 recent
    });
  }

  async testConnection(phoneNumberId: string, accessToken: string): Promise<{ success: boolean; message: string }> {
    try {
      const url = `https://graph.facebook.com/v19.0/${phoneNumberId}`;
      const response = await axios.get(url, {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      });
      if (response.data && response.data.id === phoneNumberId) {
        const displayName = response.data.display_phone_number || response.data.verified_name || 'WhatsApp Business Number';
        return {
          success: true,
          message: `Connected successfully! Display Name: ${displayName}`,
        };
      }
      return {
        success: false,
        message: 'Invalid response from Meta API',
      };
    } catch (err: any) {
      const errorMsg = err.response?.data?.error?.message || err.message;
      return {
        success: false,
        message: `Meta API connection failed: ${errorMsg}`,
      };
    }
  }
 
  private getLocalFilePath(imageUrl: string): string | null {
    if (!imageUrl) return null;
    if (imageUrl.includes('/uploads/')) {
      try {
        const parts = imageUrl.split('/uploads/');
        const filename = parts[parts.length - 1];
        const localPath = path.join(process.cwd(), 'uploads', filename);
        if (fs.existsSync(localPath)) {
          return localPath;
        }
      } catch (err: any) {
        this.logger.error(`Error checking local file path for ${imageUrl}: ${err.message}`);
      }
    }
    return null;
  }
 
  private getMimeType(filePath: string): string {
    const ext = filePath.toLowerCase().split('.').pop();
    switch (ext) {
      case 'png': return 'image/png';
      case 'webp': return 'image/webp';
      case 'gif': return 'image/gif';
      default: return 'image/jpeg';
    }
  }
 
  private async uploadMediaToMeta(
    phoneNumberId: string,
    accessToken: string,
    localFilePath: string,
    mimeType: string,
  ): Promise<string | null> {
    try {
      const fileBuffer = fs.readFileSync(localFilePath);
      const blob = new (globalThis as any).Blob([fileBuffer], { type: mimeType });
      const filename = path.basename(localFilePath);
      const file = new (globalThis as any).File([blob], filename, { type: mimeType });
 
      const form = new (globalThis as any).FormData();
      form.append('file', file);
      form.append('messaging_product', 'whatsapp');
      form.append('type', mimeType);
 
      const url = `https://graph.facebook.com/v19.0/${phoneNumberId}/media`;
      const response = await axios.post(url, form, {
        headers: {
          Authorization: `Bearer ${accessToken}`,
          'Content-Type': 'multipart/form-data',
        },
      });
 
      if (response.data && response.data.id) {
        this.logger.log(`Uploaded media successfully to Meta WABA. Media ID: ${response.data.id}`);
        return response.data.id;
      }
      return null;
    } catch (err: any) {
      const errorMsg = err.response?.data ? JSON.stringify(err.response.data) : err.message;
      this.logger.error(`Failed to upload media to Meta: ${errorMsg}`);
      return null;
    }
  }
}
