import { Injectable, Logger, Inject } from '@nestjs/common';
import { ChatHistoryStreamService } from '../chat-history-stream.service';
import { WhatsappWebService } from 'src/modules/whatsapp-web/services/whatsapp-web.service';
import {
  IWhatsappOutboundService,
  WHATSAPP_OUTBOUND_SERVICE,
} from 'src/modules/whatsapp-019/interfaces/whatsapp-outbound.interface';
import { ChatGptService } from 'src/shared/third-party/services/chat-gpt/chat-gpt.service';
import { WhatsappResponseAdapter, IWhatsappAdapterResult } from '../adapters/whatsapp-response-adapter';
import { CreateConversationDto } from '../chat-history.validation';
import { WhatsappWebWebhook } from 'src/modules/whatsapp-web/interfaces/whatsapp-web-webhook.interface';
import { IFullUser } from 'src/modules/users/users.interface';
import { InjectModel } from '@nestjs/mongoose';
import { User } from 'src/modules/users/users.model';
import { ChatHistory } from '../chat-history.model';
import { Model, Types } from 'mongoose';
import { StorageService, IImageData } from 'src/modules/storage/storage.service';
import { IConversationDto } from '../chat-history.interface';
import { WhatsappButton } from 'src/modules/whatsapp/interfaces/whatsapp-messaging.interface';
import envConfig from 'src/common/config/envConfig';

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

  constructor(
    private readonly chatHistoryStreamService: ChatHistoryStreamService,
    @Inject(WHATSAPP_OUTBOUND_SERVICE)
    private readonly whatsappWebMessagingService: IWhatsappOutboundService,
    private readonly whatsappWebService: WhatsappWebService,
    private readonly chatGptService: ChatGptService,
    private readonly storageService: StorageService,
    @InjectModel(User.name)
    private readonly userModel: Model<User>,
    @InjectModel(ChatHistory.name)
    private readonly chatHistoryModel: Model<ChatHistory>,
  ) {}

  async processWhatsappMessage(message: WhatsappWebWebhook.MessageData): Promise<void> {
    const phoneNumber = message.from_phone ?? message.from.replace(/@c\.us$/, '');
    const totalStart = Date.now();
    this.logger.log(
      `[WA-PERF] processWhatsappMessage: START phone=${phoneNumber} type=${message.message_type} messageId=${message.message_id}`,
    );

    const supportedTypes = ['text', 'image', 'document', 'audio', 'ptt'];
    if (!supportedTypes.includes(message.message_type)) {
      this.logger.warn(
        `ChatWhatsappService.processWhatsappMessage: Ignoring unsupported message type ${message.message_type} from ${phoneNumber}`,
      );
      return;
    }

    try {
      const userLookupStart = Date.now();
      const user = await this.checkNumber(phoneNumber);
      this.logger.log(
        `[WA-PERF] checkNumber: ${Date.now() - userLookupStart}ms (user=${user?._id || 'not-found'})`,
      );
      if (!user) {
        this.logger.error(
          `ChatWhatsappService.processWhatsappMessage: User not found for phone ${phoneNumber}`,
        );
        const settingsUrl = `${envConfig().FRONTEND_URL}/account-settings`;
        const errorMessage = `היי כדי להשתמש בי צריך להיות רשום לאתר בינה פלוס [חינם] , ולרשום את מספר הטלפון בחשבון , קישור לרישום מספר הטלפון באתר בינה פלוס :
${settingsUrl}

אחרי שהגדרנו מספר טלפון  באתר או באפליקציה , נשמח לשוחח פה :)`;

        await this.whatsappWebMessagingService.sendTextMessage(
          phoneNumber,
          errorMessage,
        );
        return;
      }

      const typingStart = Date.now();
      this.whatsappWebMessagingService
        .sendTyping(phoneNumber)
        .then(() => {
          this.logger.log(
            `[WA-PERF] sendTyping (fire-and-forget) finished: ${Date.now() - typingStart}ms`,
          );
        })
        .catch((err) => {
          this.logger.error(
            `[WA-PERF] sendTyping failed after ${Date.now() - typingStart}ms - ${err?.message}`,
          );
        });

      const retryMessageId = this.getRetryMessageId(message);
      const convStart = Date.now();
      const conversationId = await this.getConversationId(user, retryMessageId);
      this.logger.log(
        `[WA-PERF] getConversationId: ${Date.now() - convStart}ms (conversationId=${conversationId || 'none'})`,
      );

      if (conversationId) {
        const abortStart = Date.now();
        await this.abortPendingMessages(conversationId);
        this.logger.log(
          `[WA-PERF] abortPendingMessages: ${Date.now() - abortStart}ms`,
        );
      }

      const createStart = Date.now();
      const createChatDto = await this.createChatData(message, user, conversationId, phoneNumber, retryMessageId);
      this.logger.log(
        `[WA-PERF] createChatData: ${Date.now() - createStart}ms (type=${message.message_type})`,
      );
      if (!createChatDto) {
        return;
      }

      const adapter = new WhatsappResponseAdapter(phoneNumber);

      const aiStart = Date.now();
      await this.chatHistoryStreamService.regenarateChatbotResponse(
        createChatDto,
        null,
        user,
        null,
        retryMessageId || null,
        adapter,
      );
      this.logger.log(
        `[WA-PERF] regenarateChatbotResponse: ${Date.now() - aiStart}ms`,
      );

      const adapterResult = adapter.getResult();

      const aiMessageId = adapterResult.newResponseMessage?._id;
      if (aiMessageId && adapterResult.conversationId) {
        const isAborted = await this.isMessageAborted(adapterResult.conversationId, aiMessageId.toString());
        if (isAborted) {
          this.logger.log(`Skipping WhatsApp response - message ${aiMessageId} was aborted`);
          return;
        }
      }

      const sendStart = Date.now();
      await this.handleWhatsappResponse(adapterResult, phoneNumber, user);
      this.logger.log(
        `[WA-PERF] handleWhatsappResponse (send reply): ${Date.now() - sendStart}ms`,
      );
      this.logger.log(
        `[WA-PERF] processWhatsappMessage: DONE phone=${phoneNumber} total=${Date.now() - totalStart}ms`,
      );
    } catch (error) {
      this.logger.error(
        `[WA-PERF] processWhatsappMessage: ERROR after ${Date.now() - totalStart}ms phone=${phoneNumber} - ${error?.message || JSON.stringify(error)}`,
        error?.stack,
      );
      try {
        await this.whatsappWebMessagingService.sendTextMessage(
          phoneNumber,
          'An unexpected error occurred. Please try again later.',
        );
      } catch (sendError) {
        this.logger.error(
          `ChatWhatsappService.processWhatsappMessage: Failed to send error message - ${sendError?.message}`,
        );
      }
    }
  }

  private async createChatData(
    message: WhatsappWebWebhook.MessageData,
    user: IFullUser,
    conversationId: string | null,
    phoneNumber: string,
    retryMessageId: string | null = null,
  ): Promise<IConversationDto | null> {
    let messageContent: string | null = null;
    let fileObj: IImageData | null = null;
    let chatType: 'text' | 'file' = 'text';

    if (retryMessageId) {
      const createChatDto: IConversationDto = {
        conversationId: conversationId || null,
        messageId: retryMessageId,
      };
      return createChatDto;
    }

    if (message.message_type === 'image' || message.message_type === 'document' || message.message_type === 'audio') {
      const fileStart = Date.now();
      fileObj = await this.getFileObj(message, user, conversationId);
      this.logger.log(
        `[WA-PERF] getFileObj: ${Date.now() - fileStart}ms (type=${message.message_type})`,
      );
      if (!fileObj) {
        this.logger.warn(
          `ChatWhatsappService.createChatData: Failed to process file from message ${message.message_id}`,
        );
        await this.whatsappWebMessagingService.sendTextMessage(
          phoneNumber,
          'Sorry, I could not process your file. Please try again.',
        );
        return null;
      }
      chatType = 'file';
      messageContent = message.caption || '.';
      this.logger.log(
        `ChatWhatsappService.createChatData: Phone=${phoneNumber}, File=${fileObj.name}, Caption=${messageContent}`,
      );
    } else {
      const textStart = Date.now();
      messageContent = await this.getTextFromWhatsappMessage(message);
      this.logger.log(
        `[WA-PERF] getTextFromWhatsappMessage: ${Date.now() - textStart}ms (type=${message.message_type})`,
      );
      if (!messageContent) {
        this.logger.warn(
          `ChatWhatsappService.createChatData: Failed to get text content from message ${message.message_id}`,
        );
        await this.whatsappWebMessagingService.sendTextMessage(
          phoneNumber,
          'Sorry, I could not process your message. Please try again.',
        );
        return null;
      }
      this.logger.log(
        `ChatWhatsappService.createChatData: Phone=${phoneNumber}, Content=${messageContent}`,
      );
    }

    const createChatDto: CreateConversationDto = {
      conversationId: conversationId || null,
      chat: {
        content: messageContent || '',
        type: chatType,
        platform: 'whatsapp',
        whatsappMetaData: {
          messageId: message.message_id,
        },
        ...(fileObj && {
          file: {
            id: fileObj.id,
            name: fileObj.name,
            mimeType: fileObj.mimeType,
            size: fileObj.size,
            url: fileObj.url,
          },
        }),
        ...(message.message_type === 'ptt' && { metadata: { isVoiceMessage: true } }),
      } as any,
    };

    return createChatDto;
  }

  private async handleWhatsappResponse(
    adapterResult: IWhatsappAdapterResult,
    phoneNumber: string,
    user: IFullUser,
  ): Promise<void> {

    if (adapterResult.error?.cause) {
      this.logger.log(
        `ChatWhatsappService.handleWhatsappResponse: Error with cause occurred for ${phoneNumber} - cause: ${adapterResult.error.cause}`,
      );

      const errorMessage = adapterResult.error.message;

      if (errorMessage) {
        await this.whatsappWebMessagingService.sendTextMessage(phoneNumber, errorMessage);
        return;
      }
    }

    const newResponseMessage = adapterResult.newResponseMessage;

    if (!newResponseMessage) {
      this.logger.warn(
        `ChatWhatsappService.handleWhatsappResponse: No result available for ${phoneNumber}`,
      );
      return;
    }

    let responseText = newResponseMessage.content;
    const responseImage = newResponseMessage.images?.[0] ?? null;

    if (newResponseMessage?.blockedReason && newResponseMessage.blockedReason.length > 0) {
      this.logger.log(
        `ChatWhatsappService.handleWhatsappResponse: Error occurred - ${adapterResult}`,
      );

      if (!responseText) {
        responseText = 'An error occurred while processing your message. Please try again.';
      }

      if (newResponseMessage.isRetryAllowed) {
        const buttons: WhatsappButton[] = [
          {
            type: 'reply',
            id: 'retry|' + newResponseMessage._id,
            title: 'נסה שוב',
          }
        ];
        await this.whatsappWebMessagingService.sendButtonsMessage(phoneNumber, responseText, buttons);
      } else {
        await this.whatsappWebMessagingService.sendTextMessage(phoneNumber, responseText);
      }

      return;
    }

    if (responseImage) {
      this.logger.log(
        `ChatWhatsappService.handleWhatsappResponse: Sending image to ${phoneNumber}`,
      );
      if (!responseText) responseText = '.';

      const imageUrl = responseImage.url;
      await this.whatsappWebMessagingService.sendImageMessage(phoneNumber, imageUrl, responseText);
    } else if (responseText) {
      this.logger.log(
        `ChatWhatsappService.handleWhatsappResponse: Sending response to ${phoneNumber}`,
      );
      await this.whatsappWebMessagingService.sendTextMessage(phoneNumber, responseText);
    } else {
      this.logger.warn(
        `ChatWhatsappService.handleWhatsappResponse: No response text available for ${phoneNumber}`,
      );
    }
  }

  private async checkNumber(phoneNumber: string): Promise<IFullUser | null> {
    this.logger.log(`ChatWhatsappService.checkNumber: Checking phone number ${phoneNumber}`);


    const user = await this.userModel
      .findOne({ phoneNumber: phoneNumber })
      .populate('subscription', '-__v -user')
      .select('-__v -password')
      .lean()
      .exec();

    if (!user) {
      this.logger.warn(`ChatWhatsappService.checkNumber: No user found for phone number ${phoneNumber}`);
      return null;
    }

    this.logger.log(`ChatWhatsappService.checkNumber: Found user ${user._id} for phone ${phoneNumber}`);
    return user as any as IFullUser;
  }

  private getRetryMessageId(message: WhatsappWebWebhook.MessageData): string | null {
    this.logger.log(
      `ChatWhatsappService.getRetryMessageId: Wizzo API does not support interactive button replies - retry via buttons not available`,
    );
    return null;
  }

  private async getConversationId(user: IFullUser, retryMessageId: string | null = null): Promise<string | null> {
    this.logger.log(
      `ChatWhatsappService.getConversationId: Getting conversation ID for user ${user._id}, retryMessageId=${retryMessageId || 'null'}`,
    );

    if (retryMessageId) {
      try {
        const userId = typeof user._id === 'string' ? new Types.ObjectId(user._id) : user._id;
        const messageObjectId = new Types.ObjectId(retryMessageId);

        const conversation = await this.chatHistoryModel.findOne({
          user: userId,
          'history._id': messageObjectId,
          'history.platform': 'whatsapp',
        }).select('_id').lean().exec();

        if (conversation) {
          const conversationId = conversation._id.toString();
          this.logger.log(
            `ChatWhatsappService.getConversationId: Found conversation ${conversationId} for retry message ${retryMessageId}`,
          );
          return conversationId;
        } else {
          this.logger.warn(
            `ChatWhatsappService.getConversationId: Conversation not found for retry message ${retryMessageId}, falling back to default logic`,
          );
        }
      } catch (error) {
        this.logger.error(
          `ChatWhatsappService.getConversationId: Error finding conversation for retry message - ${error?.message || JSON.stringify(error)}`,
          error?.stack,
        );
      }
    }

    const timeSessionLimit = new Date(Date.now() - 10 * 60 * 1000);

    try {
      const result = await this.chatHistoryModel.aggregate([
        {
          $match: {
            user: user._id,
            'history.platform': 'whatsapp',
          },
        },
        {
          $unwind: '$history',
        },
        {
          $match: {
            'history.platform': 'whatsapp',
            'history.createdAt': { $gte: timeSessionLimit },
          },
        },
        {
          $sort: { 'history.createdAt': -1 },
        },
        {
          $group: {
            _id: '$_id',
            mostRecentMessageDate: { $first: '$history.createdAt' },
            conversationId: { $first: '$_id' },
          },
        },
        {
          $sort: { mostRecentMessageDate: -1 },
        },
        {
          $limit: 1,
        },
      ]);

      if (!result || result.length === 0) {
        this.logger.log(`ChatWhatsappService.getConversationId: No WhatsApp conversations found in last 10 minutes for user ${user._id}`);
        return null;
      }

      const conversationId = result[0].conversationId?.toString();
      this.logger.log(
        `ChatWhatsappService.getConversationId: Found conversation ${conversationId} with most recent WhatsApp message at ${result[0].mostRecentMessageDate}`,
      );
      return conversationId || null;
    } catch (error) {
      this.logger.error(
        `ChatWhatsappService.getConversationId: Error finding conversation - ${error?.message || JSON.stringify(error)}`,
        error?.stack,
      );
      return null;
    }
  }

  private async getTextFromWhatsappMessage(
    message: WhatsappWebWebhook.MessageData,
  ): Promise<string | null> {
    try {
      if (message.message_type === 'text') {
        if (!message.content) {
          this.logger.warn(
            `ChatWhatsappService.getTextFromWhatsappMessage: Text message has no content`,
          );
          return null;
        }
        return message.content;
      }

      if (message.message_type === 'ptt') {
        if (!message.content) {
          this.logger.warn(
            `ChatWhatsappService.getTextFromWhatsappMessage: Audio message has no content`,
          );
          return null;
        }

        this.logger.log(
          `ChatWhatsappService.getTextFromWhatsappMessage: Handling incoming ${message.message_type} message`,
        );

        const parsed = this.whatsappWebService.parseBase64Content(message.content, message.message_type);
        if (!parsed) {
          this.logger.error(
            `ChatWhatsappService.getTextFromWhatsappMessage: Failed to parse base64 content for ${message.message_type}`,
          );
          return null;
        }

        const audioFile = {
          buffer: parsed.buffer,
          originalname: `audio_${message.message_id}.${this.getAudioFileExtension(parsed.mimeType)}`,
          mimetype: parsed.mimeType,
          size: parsed.buffer.length,
        };

        this.logger.log(
          `ChatWhatsappService.getTextFromWhatsappMessage: Transcribing audio file: ${audioFile.originalname}, size: ${audioFile.size} bytes`,
        );

        const transcribeStart = Date.now();
        const transcribedText = await this.chatGptService.transcribeAudio(audioFile);
        this.logger.log(
          `[WA-PERF] transcribeAudio: ${Date.now() - transcribeStart}ms (size=${audioFile.size}b)`,
        );
        this.logger.log(
          `ChatWhatsappService.getTextFromWhatsappMessage: Audio transcribed successfully: ${transcribedText}`,
        );
        return transcribedText;
      }

      this.logger.warn(
        `ChatWhatsappService.getTextFromWhatsappMessage: Unsupported message type: ${message.message_type}`,
      );
      return null;
    } catch (error) {
      this.logger.error(
        `ChatWhatsappService.getTextFromWhatsappMessage: Error processing message - ${error?.message || JSON.stringify(error)}`,
        error?.stack,
      );
      return null;
    }
  }

  private getAudioFileExtension(mimeType: string): string {
    const mimeToExt: { [key: string]: string } = {
      'audio/ogg': 'ogg',
      'audio/webm': 'webm',
      'audio/mpeg': 'mp3',
      'audio/mp4': 'm4a',
      'audio/wav': 'wav',
      'audio/x-ms-wma': 'wma',
      'audio/aac': 'aac',
    };
    return mimeToExt[mimeType] || 'webm';
  }

  private async getFileObj(
    message: WhatsappWebWebhook.MessageData,
    user: IFullUser,
    conversationId: string | null,
  ): Promise<IImageData | null> {
    try {
      if (!message.content) {
        this.logger.warn(
          `ChatWhatsappService.getFileObj: No content found for message type ${message.message_type}`,
        );
        return null;
      }

      this.logger.log(
        `ChatWhatsappService.getFileObj: Parsing base64 content for message_type=${message.message_type}, message_id=${message.message_id}`,
      );

      const parsed = this.whatsappWebService.parseBase64Content(message.content, message.message_type);
      if (!parsed) {
        this.logger.error(
          `ChatWhatsappService.getFileObj: Failed to parse base64 content for message ${message.message_id}`,
        );
        return null;
      }

      const fileExtension = this.getFileExtensionFromMimeType(parsed.mimeType);
      const finalFilename = `file_${message.message_id}.${fileExtension}`;

      const file = {
        buffer: parsed.buffer,
        originalname: finalFilename,
        mimetype: parsed.mimeType,
        size: parsed.buffer.length,
      };

      this.logger.log(
        `ChatWhatsappService.getFileObj: Uploading file to storage - name=${file.originalname}, size=${file.size} bytes, mimeType=${file.mimetype}`,
      );

      const uploadStart = Date.now();
      const storageResult = await this.storageService.uploadGoogleCloudStorage(
        file,
        {
          userId: user._id.toString(),
          userEmail: user.email,
          ...(conversationId && { conversationId }),
          messageId: message.message_id,
        },
        true,
        true,
      );
      this.logger.log(
        `[WA-PERF] uploadGoogleCloudStorage: ${Date.now() - uploadStart}ms (size=${file.size}b)`,
      );

      this.logger.log(
        `ChatWhatsappService.getFileObj: File uploaded successfully - id=${storageResult.id}, url=${storageResult.url}`,
      );

      return storageResult;
    } catch (error) {
      this.logger.error(
        `ChatWhatsappService.getFileObj: Error processing file - ${error?.message || JSON.stringify(error)}`,
        error?.stack,
      );
      return null;
    }
  }

  private async abortPendingMessages(conversationId: string): Promise<void> {
    try {
      const result = await this.chatHistoryModel.updateOne(
        { _id: conversationId },
        { $set: { 'history.$[elem].status': 'aborted' } },
        {
          arrayFilters: [
            {
              'elem.status': 'loading',
              'elem.role': 'system',
              'elem.type': { $ne: 'file' },
            },
          ],
        },
      );
      if (result.modifiedCount > 0) {
        this.logger.log(
          `ChatWhatsappService.abortPendingMessages: Aborted pending messages in conversation ${conversationId}`,
        );
      }
    } catch (error) {
      this.logger.error(
        `ChatWhatsappService.abortPendingMessages: Error aborting pending messages - ${error?.message}`,
        error?.stack,
      );
    }
  }

  private async isMessageAborted(conversationId: string, messageId: string): Promise<boolean> {
    try {
      // Use $elemMatch so both predicates target the SAME history entry.
      // Without it Mongo would match a doc where *any* entry has the given _id
      // and *any (possibly different)* entry has status=aborted — which fires
      // a false positive whenever the conversation contains an older aborted
      // message.
      const conversation = await this.chatHistoryModel.findOne(
        {
          _id: conversationId,
          history: {
            $elemMatch: {
              _id: new Types.ObjectId(messageId),
              status: 'aborted',
            },
          },
        },
        { _id: 1 },
      ).lean().exec();

      return !!conversation;
    } catch (error) {
      this.logger.error(
        `ChatWhatsappService.isMessageAborted: Error checking abort status - ${error?.message}`,
        error?.stack,
      );
      return false;
    }
  }

  private getFileExtensionFromMimeType(mimeType: string): string {
    const mimeToExt: { [key: string]: string } = {
      'image/jpeg': 'jpg',
      'image/jpg': 'jpg',
      'image/png': 'png',
      'image/gif': 'gif',
      'image/webp': 'webp',
      'application/pdf': 'pdf',
      'application/msword': 'doc',
      'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',
      'application/vnd.ms-excel': 'xls',
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',
      'application/vnd.ms-powerpoint': 'ppt',
      'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx',
      'text/plain': 'txt',
      'text/csv': 'csv',
      'audio/ogg': 'ogg',
      'audio/webm': 'webm',
      'audio/mpeg': 'mp3',
      'audio/mp4': 'm4a',
      'audio/wav': 'wav',
      'audio/x-ms-wma': 'wma',
      'audio/aac': 'aac',
    };
    return mimeToExt[mimeType] || 'bin';
  }

  
}
