import { InstanceDto } from '@api/dto/instance.dto';
import {
  MediaMessage,
  Options,
  SendAudioDto,
  SendButtonsDto,
  SendMediaDto,
  SendTextDto,
} from '@api/dto/sendMessage.dto';
import * as s3Service from '@api/integrations/storage/s3/libs/minio.server';
import { PrismaRepository } from '@api/repository/repository.service';
import { chatbotController } from '@api/server.module';
import { CacheService } from '@api/services/cache.service';
import { ChannelStartupService } from '@api/services/channel.service';
import { Events, wa } from '@api/types/wa.types';
import { AudioConverter, Chatwoot, ConfigService, Openai, S3 } from '@config/env.config';
import { BadRequestException, InternalServerErrorException } from '@exceptions';
import { createJid } from '@utils/createJid';
import axios from 'axios';
import { isBase64, isURL } from 'class-validator';
import EventEmitter2 from 'eventemitter2';
import FormData from 'form-data';
import mimeTypes from 'mime-types';
import { join } from 'path';
import { v4 } from 'uuid';

export class EvolutionStartupService extends ChannelStartupService {
  constructor(
    public readonly configService: ConfigService,
    public readonly eventEmitter: EventEmitter2,
    public readonly prismaRepository: PrismaRepository,
    public readonly cache: CacheService,
    public readonly chatwootCache: CacheService,
  ) {
    super(configService, eventEmitter, prismaRepository, chatwootCache);

    this.client = null;
  }

  public client: any;

  public stateConnection: wa.StateConnection = { state: 'open' };

  public phoneNumber: string;
  public mobile: boolean;

  public get connectionStatus() {
    return this.stateConnection;
  }

  public async closeClient() {
    this.stateConnection = { state: 'close' };
  }

  public get qrCode(): wa.QrCode {
    return {
      pairingCode: this.instance.qrcode?.pairingCode,
      code: this.instance.qrcode?.code,
      base64: this.instance.qrcode?.base64,
      count: this.instance.qrcode?.count,
    };
  }

  public async logoutInstance() {
    await this.closeClient();
  }

  public setInstance(instance: InstanceDto) {
    this.logger.setInstance(instance.instanceId);

    this.instance.name = instance.instanceName;
    this.instance.id = instance.instanceId;
    this.instance.integration = instance.integration;
    this.instance.number = instance.number;
    this.instance.token = instance.token;
    this.instance.businessId = instance.businessId;

    if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
      this.chatwootService.eventWhatsapp(
        Events.STATUS_INSTANCE,
        {
          instanceName: this.instance.name,
          instanceId: this.instance.id,
          integration: instance.integration,
        },
        {
          instance: this.instance.name,
          status: 'created',
        },
      );
    }
  }

  public async profilePicture(number: string) {
    const jid = createJid(number);

    return {
      wuid: jid,
      profilePictureUrl: null,
    };
  }

  public async getProfileName() {
    return null;
  }

  public async profilePictureUrl() {
    return null;
  }

  public async getProfileStatus() {
    return null;
  }

  public async connectToWhatsapp(data?: any): Promise<any> {
    if (!data) {
      this.loadChatwoot();
      return;
    }

    try {
      this.eventHandler(data);
    } catch (error) {
      this.logger.error(error);
      throw new InternalServerErrorException(error?.toString());
    }
  }

  protected async eventHandler(received: any) {
    try {
      let messageRaw: any;

      if (received.message) {
        const key = {
          id: received.key.id || v4(),
          remoteJid: received.key.remoteJid,
          fromMe: received.key.fromMe,
          profilePicUrl: received.profilePicUrl,
        };
        messageRaw = {
          key,
          pushName: received.pushName,
          message: received.message,
          messageType: received.messageType,
          messageTimestamp: Math.round(new Date().getTime() / 1000),
          source: 'unknown',
          instanceId: this.instanceId,
        };

        const isAudio = received?.message?.audioMessage;

        if (this.configService.get<Openai>('OPENAI').ENABLED && isAudio) {
          const openAiDefaultSettings = await this.prismaRepository.openaiSetting.findFirst({
            where: {
              instanceId: this.instanceId,
            },
            include: {
              OpenaiCreds: true,
            },
          });

          if (
            openAiDefaultSettings &&
            openAiDefaultSettings.openaiCredsId &&
            openAiDefaultSettings.speechToText &&
            received?.message?.audioMessage
          ) {
            messageRaw.message.speechToText = `[audio] ${await this.openaiService.speechToText(received, this)}`;
          }
        }

        this.logger.log(messageRaw);

        this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw);

        await chatbotController.emit({
          instance: { instanceName: this.instance.name, instanceId: this.instanceId },
          remoteJid: messageRaw.key.remoteJid,
          msg: messageRaw,
          pushName: messageRaw.pushName,
        });

        if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
          const chatwootSentMessage = await this.chatwootService.eventWhatsapp(
            Events.MESSAGES_UPSERT,
            { instanceName: this.instance.name, instanceId: this.instanceId },
            messageRaw,
          );

          if (chatwootSentMessage?.id) {
            messageRaw.chatwootMessageId = chatwootSentMessage.id;
            messageRaw.chatwootInboxId = chatwootSentMessage.id;
            messageRaw.chatwootConversationId = chatwootSentMessage.id;
          }
        }

        await this.prismaRepository.message.create({
          data: messageRaw,
        });

        await this.updateContact({
          remoteJid: messageRaw.key.remoteJid,
          pushName: messageRaw.pushName,
          profilePicUrl: received.profilePicUrl,
        });
      }
    } catch (error) {
      this.logger.error(error);
    }
  }

  private async updateContact(data: { remoteJid: string; pushName?: string; profilePicUrl?: string }) {
    const contactRaw: any = {
      remoteJid: data.remoteJid,
      pushName: data?.pushName,
      instanceId: this.instanceId,
      profilePicUrl: data?.profilePicUrl,
    };

    const existingContact = await this.prismaRepository.contact.findFirst({
      where: {
        remoteJid: data.remoteJid,
        instanceId: this.instanceId,
      },
    });

    if (existingContact) {
      await this.prismaRepository.contact.updateMany({
        where: {
          remoteJid: data.remoteJid,
          instanceId: this.instanceId,
        },
        data: contactRaw,
      });
    } else {
      await this.prismaRepository.contact.create({
        data: contactRaw,
      });
    }

    this.sendDataWebhook(Events.CONTACTS_UPSERT, contactRaw);

    if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
      await this.chatwootService.eventWhatsapp(
        Events.CONTACTS_UPDATE,
        {
          instanceName: this.instance.name,
          instanceId: this.instanceId,
          integration: this.instance.integration,
        },
        contactRaw,
      );
    }

    const chat = await this.prismaRepository.chat.findFirst({
      where: { instanceId: this.instanceId, remoteJid: data.remoteJid },
    });

    if (chat) {
      const chatRaw: any = {
        remoteJid: data.remoteJid,
        instanceId: this.instanceId,
      };

      this.sendDataWebhook(Events.CHATS_UPDATE, chatRaw);

      await this.prismaRepository.chat.updateMany({
        where: { remoteJid: chat.remoteJid },
        data: chatRaw,
      });
    }

    const chatRaw: any = {
      remoteJid: data.remoteJid,
      instanceId: this.instanceId,
    };

    this.sendDataWebhook(Events.CHATS_UPSERT, chatRaw);

    await this.prismaRepository.chat.create({
      data: chatRaw,
    });
  }

  protected async sendMessageWithTyping(
    number: string,
    message: any,
    options?: Options,
    file?: any,
    isIntegration = false,
  ) {
    try {
      let quoted: any;
      let webhookUrl: any;

      if (options?.quoted) {
        const m = options?.quoted;

        const msg = m?.key;

        if (!msg) {
          throw 'Message not found';
        }

        quoted = msg;
      }

      if (options.delay) {
        await new Promise((resolve) => setTimeout(resolve, options.delay));
      }

      if (options?.webhookUrl) {
        webhookUrl = options.webhookUrl;
      }

      let audioFile;

      const messageId = v4();

      let messageRaw: any;

      if (message?.mediaType === 'image') {
        messageRaw = {
          key: { fromMe: true, id: messageId, remoteJid: number },
          message: {
            base64: isBase64(message.media) ? message.media : null,
            mediaUrl: isURL(message.media) ? message.media : null,
            quoted,
          },
          messageType: 'imageMessage',
          messageTimestamp: Math.round(new Date().getTime() / 1000),
          webhookUrl,
          source: 'unknown',
          instanceId: this.instanceId,
        };
      } else if (message?.mediaType === 'video') {
        messageRaw = {
          key: { fromMe: true, id: messageId, remoteJid: number },
          message: {
            base64: isBase64(message.media) ? message.media : null,
            mediaUrl: isURL(message.media) ? message.media : null,
            quoted,
          },
          messageType: 'videoMessage',
          messageTimestamp: Math.round(new Date().getTime() / 1000),
          webhookUrl,
          source: 'unknown',
          instanceId: this.instanceId,
        };
      } else if (message?.mediaType === 'audio') {
        messageRaw = {
          key: { fromMe: true, id: messageId, remoteJid: number },
          message: {
            base64: isBase64(message.media) ? message.media : null,
            mediaUrl: isURL(message.media) ? message.media : null,
            quoted,
          },
          messageType: 'audioMessage',
          messageTimestamp: Math.round(new Date().getTime() / 1000),
          webhookUrl,
          source: 'unknown',
          instanceId: this.instanceId,
        };

        const buffer = Buffer.from(message.media, 'base64');
        audioFile = {
          buffer,
          mimetype: 'audio/mp4',
          originalname: `${messageId}.mp4`,
        };
      } else if (message?.mediaType === 'document') {
        messageRaw = {
          key: { fromMe: true, id: messageId, remoteJid: number },
          message: {
            base64: isBase64(message.media) ? message.media : null,
            mediaUrl: isURL(message.media) ? message.media : null,
            quoted,
          },
          messageType: 'documentMessage',
          messageTimestamp: Math.round(new Date().getTime() / 1000),
          webhookUrl,
          source: 'unknown',
          instanceId: this.instanceId,
        };
      } else if (message.buttonMessage) {
        messageRaw = {
          key: { fromMe: true, id: messageId, remoteJid: number },
          message: {
            ...message.buttonMessage,
            buttons: message.buttonMessage.buttons,
            footer: message.buttonMessage.footer,
            body: message.buttonMessage.body,
            quoted,
          },
          messageType: 'buttonMessage',
          messageTimestamp: Math.round(new Date().getTime() / 1000),
          webhookUrl,
          source: 'unknown',
          instanceId: this.instanceId,
        };
      } else if (message.listMessage) {
        messageRaw = {
          key: { fromMe: true, id: messageId, remoteJid: number },
          message: {
            ...message.listMessage,
            quoted,
          },
          messageType: 'listMessage',
          messageTimestamp: Math.round(new Date().getTime() / 1000),
          webhookUrl,
          source: 'unknown',
          instanceId: this.instanceId,
        };
      } else {
        messageRaw = {
          key: { fromMe: true, id: messageId, remoteJid: number },
          message: {
            ...message,
            quoted,
          },
          messageType: 'conversation',
          messageTimestamp: Math.round(new Date().getTime() / 1000),
          webhookUrl,
          source: 'unknown',
          instanceId: this.instanceId,
        };
      }

      if (messageRaw.message.contextInfo) {
        messageRaw.contextInfo = {
          ...messageRaw.message.contextInfo,
        };
      }

      if (messageRaw.contextInfo?.stanzaId) {
        const key: any = {
          id: messageRaw.contextInfo.stanzaId,
        };

        const findMessage = await this.prismaRepository.message.findFirst({
          where: {
            instanceId: this.instanceId,
            key,
          },
        });

        if (findMessage) {
          messageRaw.contextInfo.quotedMessage = findMessage.message;
        }
      }

      const { base64 } = messageRaw.message;
      delete messageRaw.message.base64;

      if (base64 || file || audioFile) {
        if (this.configService.get<S3>('S3').ENABLE) {
          try {
            // Verificação adicional para garantir que há conteúdo de mídia real
            const hasRealMedia = this.hasValidMediaContent(messageRaw);

            if (!hasRealMedia) {
              this.logger.warn('Message detected as media but contains no valid media content');
            } else {
              const fileBuffer = audioFile?.buffer || file?.buffer;
              const buffer = base64 ? Buffer.from(base64, 'base64') : fileBuffer;

              let mediaType: string;
              let mimetype = audioFile?.mimetype || file.mimetype;

              if (messageRaw.messageType === 'documentMessage') {
                mediaType = 'document';
                mimetype = !mimetype ? 'application/pdf' : mimetype;
              } else if (messageRaw.messageType === 'imageMessage') {
                mediaType = 'image';
                mimetype = !mimetype ? 'image/png' : mimetype;
              } else if (messageRaw.messageType === 'audioMessage') {
                mediaType = 'audio';
                mimetype = !mimetype ? 'audio/mp4' : mimetype;
              } else if (messageRaw.messageType === 'videoMessage') {
                mediaType = 'video';
                mimetype = !mimetype ? 'video/mp4' : mimetype;
              }

              const fileName = `${messageRaw.key.id}.${mimetype.split('/')[1]}`;

              const size = buffer.byteLength;

              const fullName = join(`${this.instance.id}`, messageRaw.key.remoteJid, mediaType, fileName);

              await s3Service.uploadFile(fullName, buffer, size, {
                'Content-Type': mimetype,
              });

              const mediaUrl = await s3Service.getObjectUrl(fullName);

              messageRaw.message.mediaUrl = mediaUrl;
            }
          } catch (error) {
            this.logger.error(['Error on upload file to minio', error?.message, error?.stack]);
          }
        }
      }

      this.logger.log(messageRaw);

      this.sendDataWebhook(Events.SEND_MESSAGE, messageRaw);

      if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled && !isIntegration) {
        this.chatwootService.eventWhatsapp(
          Events.SEND_MESSAGE,
          { instanceName: this.instance.name, instanceId: this.instanceId },
          messageRaw,
        );
      }

      if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled && isIntegration)
        await chatbotController.emit({
          instance: { instanceName: this.instance.name, instanceId: this.instanceId },
          remoteJid: messageRaw.key.remoteJid,
          msg: messageRaw,
          pushName: messageRaw.pushName,
        });

      await this.prismaRepository.message.create({
        data: messageRaw,
      });

      return messageRaw;
    } catch (error) {
      this.logger.error(error);
      throw new BadRequestException(error.toString());
    }
  }

  public async textMessage(data: SendTextDto, isIntegration = false) {
    const res = await this.sendMessageWithTyping(
      data.number,
      {
        conversation: data.text,
      },
      {
        delay: data?.delay,
        presence: 'composing',
        quoted: data?.quoted,
        linkPreview: data?.linkPreview,
        mentionsEveryOne: data?.mentionsEveryOne,
        mentioned: data?.mentioned,
      },
      null,
      isIntegration,
    );
    return res;
  }

  protected async prepareMediaMessage(mediaMessage: MediaMessage) {
    try {
      if (mediaMessage.mediatype === 'document' && !mediaMessage.fileName) {
        const regex = new RegExp(/.*\/(.+?)\./);
        const arrayMatch = regex.exec(mediaMessage.media);
        mediaMessage.fileName = arrayMatch[1];
      }

      if (mediaMessage.mediatype === 'image' && !mediaMessage.fileName) {
        mediaMessage.fileName = 'image.png';
      }

      if (mediaMessage.mediatype === 'video' && !mediaMessage.fileName) {
        mediaMessage.fileName = 'video.mp4';
      }

      let mimetype: string | false;

      const prepareMedia: any = {
        caption: mediaMessage?.caption,
        fileName: mediaMessage.fileName,
        mediaType: mediaMessage.mediatype,
        media: mediaMessage.media,
        gifPlayback: false,
      };

      if (isURL(mediaMessage.media)) {
        mimetype = mimeTypes.lookup(mediaMessage.media);
      } else {
        mimetype = mimeTypes.lookup(mediaMessage.fileName);
      }

      prepareMedia.mimetype = mimetype;

      return prepareMedia;
    } catch (error) {
      this.logger.error(error);
      throw new InternalServerErrorException(error?.toString() || error);
    }
  }

  public async mediaMessage(data: SendMediaDto, file?: any, isIntegration = false) {
    const mediaData: SendMediaDto = { ...data };

    if (file) mediaData.media = file.buffer.toString('base64');

    const message = await this.prepareMediaMessage(mediaData);

    const mediaSent = await this.sendMessageWithTyping(
      data.number,
      { ...message },
      {
        delay: data?.delay,
        presence: 'composing',
        quoted: data?.quoted,
        linkPreview: data?.linkPreview,
        mentionsEveryOne: data?.mentionsEveryOne,
        mentioned: data?.mentioned,
      },
      file,
      isIntegration,
    );

    return mediaSent;
  }

  public async processAudio(audio: string, number: string, file: any) {
    number = number.replace(/\D/g, '');
    const hash = `${number}-${new Date().getTime()}`;

    const audioConverterConfig = this.configService.get<AudioConverter>('AUDIO_CONVERTER');
    if (audioConverterConfig.API_URL) {
      try {
        this.logger.verbose('Using audio converter API');
        const formData = new FormData();

        if (file) {
          formData.append('file', file.buffer, {
            filename: file.originalname,
            contentType: file.mimetype,
          });
        } else if (isURL(audio)) {
          formData.append('url', audio);
        } else {
          formData.append('base64', audio);
        }

        formData.append('format', 'mp4');

        const response = await axios.post(audioConverterConfig.API_URL, formData, {
          headers: {
            ...formData.getHeaders(),
            apikey: audioConverterConfig.API_KEY,
          },
        });

        if (!response?.data?.audio) {
          throw new InternalServerErrorException('Failed to convert audio');
        }

        const prepareMedia: any = {
          fileName: `${hash}.mp4`,
          mediaType: 'audio',
          media: response?.data?.audio,
          mimetype: 'audio/mpeg',
        };

        return prepareMedia;
      } catch (error) {
        this.logger.error(error?.response?.data || error);
        throw new InternalServerErrorException(error?.response?.data?.message || error?.toString() || error);
      }
    } else {
      let mimetype: string;

      const prepareMedia: any = {
        fileName: `${hash}.mp3`,
        mediaType: 'audio',
        media: audio,
        mimetype: 'audio/mpeg',
      };

      if (isURL(audio)) {
        mimetype = mimeTypes.lookup(audio).toString();
      } else {
        mimetype = mimeTypes.lookup(prepareMedia.fileName).toString();
      }

      prepareMedia.mimetype = mimetype;

      return prepareMedia;
    }
  }

  public async audioWhatsapp(data: SendAudioDto, file?: any, isIntegration = false) {
    const mediaData: SendAudioDto = { ...data };

    if (file?.buffer) {
      mediaData.audio = file.buffer.toString('base64');
    } else {
      console.error('El archivo o buffer no est� definido correctamente.');
      throw new Error('File or buffer is undefined.');
    }

    const message = await this.processAudio(mediaData.audio, data.number, file);

    const audioSent = await this.sendMessageWithTyping(
      data.number,
      { ...message },
      {
        delay: data?.delay,
        presence: 'composing',
        quoted: data?.quoted,
        linkPreview: data?.linkPreview,
        mentionsEveryOne: data?.mentionsEveryOne,
        mentioned: data?.mentioned,
      },
      file,
      isIntegration,
    );

    return audioSent;
  }

  public async buttonMessage(data: SendButtonsDto, isIntegration = false) {
    return await this.sendMessageWithTyping(
      data.number,
      {
        buttonMessage: {
          title: data.title,
          description: data.description,
          footer: data.footer,
          buttons: data.buttons,
        },
      },
      {
        delay: data?.delay,
        presence: 'composing',
        quoted: data?.quoted,
        mentionsEveryOne: data?.mentionsEveryOne,
        mentioned: data?.mentioned,
      },
      null,
      isIntegration,
    );
  }
  public async locationMessage() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async listMessage() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async templateMessage() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async contactMessage() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async reactionMessage() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async getBase64FromMediaMessage() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async deleteMessage() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async mediaSticker() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async pollMessage() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async statusMessage() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async reloadConnection() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async whatsappNumber() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async markMessageAsRead() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async archiveChat() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async markChatUnread() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async fetchProfile() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async offerCall() {
    throw new BadRequestException('Method not available on WhatsApp Business API');
  }
  public async sendPresence() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async setPresence() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async fetchPrivacySettings() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async updatePrivacySettings() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async fetchBusinessProfile() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async updateProfileName() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async updateProfileStatus() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async updateProfilePicture() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async removeProfilePicture() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async blockUser() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async updateMessage() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async createGroup() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async updateGroupPicture() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async updateGroupSubject() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async updateGroupDescription() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async findGroup() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async fetchAllGroups() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async inviteCode() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async inviteInfo() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async sendInvite() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async acceptInviteCode() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async revokeInviteCode() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async findParticipants() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async updateGParticipant() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async updateGSetting() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async toggleEphemeral() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async leaveGroup() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async fetchLabels() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async handleLabel() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async receiveMobileCode() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
  public async fakeCall() {
    throw new BadRequestException('Method not available on Evolution Channel');
  }
}
