module Whatsapp::BaileysHandlers::Helpers # rubocop:disable Metrics/ModuleLength
  include Whatsapp::IncomingMessageServiceHelpers

  private

  def unwrap_ephemeral_message(msg)
    msg.key?(:ephemeralMessage) ? msg.dig(:ephemeralMessage, :message) : msg
  end

  def raw_message_id
    @raw_message[:key][:id]
  end

  def incoming?
    !@raw_message[:key][:fromMe]
  end

  def jid_type # rubocop:disable Metrics/CyclomaticComplexity
    jid = @raw_message[:key][:remoteJid]
    server = jid.split('@').last

    # NOTE: Based on Baileys internal functions
    # https://github.com/WhiskeySockets/Baileys/blob/v6.7.16/src/WABinary/jid-utils.ts#L48-L58
    case server
    when 's.whatsapp.net', 'c.us'
      'user'
    when 'g.us'
      'group'
    when 'lid'
      'lid'
    when 'broadcast'
      jid.start_with?('status@') ? 'status' : 'broadcast'
    when 'newsletter'
      'newsletter'
    when 'call'
      'call'
    else
      'unknown'
    end
  end

  def message_type # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity,Metrics/MethodLength,Metrics/AbcSize
    msg = unwrap_ephemeral_message(@raw_message[:message])
    if msg.key?(:conversation) || msg.dig(:extendedTextMessage, :text).present?
      'text'
    elsif msg.key?(:imageMessage)
      'image'
    elsif msg.key?(:audioMessage)
      'audio'
    elsif msg.key?(:videoMessage)
      'video'
    elsif msg.key?(:documentMessage) || msg.key?(:documentWithCaptionMessage)
      'file'
    elsif msg.key?(:stickerMessage)
      'sticker'
    elsif msg.key?(:reactionMessage)
      'reaction'
    elsif msg.key?(:editedMessage)
      'edited'
    elsif msg.key?(:contactMessage)
      match_phone_number = msg.dig(:contactMessage, :vcard)&.match(/waid=(\d+)/)
      match_phone_number ? 'contact' : 'unsupported'
    elsif msg.key?(:protocolMessage)
      'protocol'
    elsif msg.key?(:messageContextInfo) && msg.keys.count == 1
      'context'
    else
      'unsupported'
    end
  end

  def message_content # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity,Metrics/MethodLength
    msg = unwrap_ephemeral_message(@raw_message[:message])
    case message_type
    when 'text'
      msg[:conversation] || msg.dig(:extendedTextMessage, :text)
    when 'image'
      msg.dig(:imageMessage, :caption)
    when 'video'
      msg.dig(:videoMessage, :caption)
    when 'file'
      msg.dig(:documentMessage, :caption).presence ||
        msg.dig(:documentWithCaptionMessage, :message, :documentMessage, :caption)
    when 'reaction'
      msg.dig(:reactionMessage, :text)
    when 'contact'
      # FIXME: Missing specs
      display_name = msg.dig(:contactMessage, :displayName)
      vcard = msg.dig(:contactMessage, :vcard)
      match_phone_number = vcard&.match(/waid=(\d+)/)

      return display_name unless match_phone_number
      return match_phone_number[1] if display_name&.start_with?('+')

      "#{display_name} - #{match_phone_number[1]}" if match_phone_number
    end
  end

  def reply_to_message_id # rubocop:disable Metrics/CyclomaticComplexity
    msg = unwrap_ephemeral_message(@raw_message[:message])
    message_key = case message_type
                  when 'text' then :extendedTextMessage
                  when 'image' then :imageMessage
                  when 'sticker' then :stickerMessage
                  when 'audio' then :audioMessage
                  when 'video' then :videoMessage
                  when 'contact' then :contactMessage
                  when 'file'
                    context_info = msg.dig(:documentMessage, :contextInfo).presence ||
                                   msg.dig(:documentWithCaptionMessage, :message, :documentMessage, :contextInfo)
                    return context_info&.dig(:stanzaId)
                  end

    msg.dig(message_key, :contextInfo, :stanzaId) if message_key
  end

  def file_content_type
    return :image if message_type.in?(%w[image sticker])
    return :video if message_type.in?(%w[video video_note])
    return :audio if message_type == 'audio'

    :file
  end

  def message_mimetype
    msg = unwrap_ephemeral_message(@raw_message[:message])
    case message_type
    when 'image'
      msg.dig(:imageMessage, :mimetype)
    when 'sticker'
      msg.dig(:stickerMessage, :mimetype)
    when 'video'
      msg.dig(:videoMessage, :mimetype)
    when 'audio'
      msg.dig(:audioMessage, :mimetype)
    when 'file'
      msg.dig(:documentMessage, :mimetype).presence ||
        msg.dig(:documentWithCaptionMessage, :message, :documentMessage, :mimetype)
    end
  end

  def extract_from_jid(type:)
    addressing_mode = @raw_message[:key][:addressingMode]
    reference_field = addressing_mode && addressing_mode != type ? :remoteJidAlt : :remoteJid

    jid = @raw_message[:key][reference_field]
    return unless jid

    # NOTE: jid shape is `<user>_<agent>:<device>@<server>`
    # https://github.com/WhiskeySockets/Baileys/blob/v7.0.0-rc.6/src/WABinary/jid-utils.ts#L52
    jid.split('@').first.split(':').first.split('_').first
  end

  def contact_name
    # NOTE: `verifiedBizName` is only available for business accounts and has a higher priority than `pushName`.
    name = @raw_message[:verifiedBizName].presence || @raw_message[:pushName]
    return name if name.presence && (self_message? || incoming?)

    extract_from_jid(type: 'pn') || extract_from_jid(type: 'lid')
  end

  def self_message?
    normalize_phone_number(extract_from_jid(type: 'pn')) == normalize_phone_number(inbox.channel.phone_number.delete('+'))
  end

  def normalize_phone_number(phone_number)
    return unless phone_number

    Whatsapp::PhoneNormalizers::BrazilPhoneNormalizer.new.normalize(phone_number)
  end

  def ignore_message?
    message_type.in?(%w[protocol context edited]) ||
      (message_type == 'reaction' && message_content.blank?)
  end

  def fetch_profile_picture_url(phone_number)
    jid = "#{phone_number}@s.whatsapp.net"
    response = inbox.channel.provider_service.get_profile_pic(jid)
    response&.dig('data', 'profilePictureUrl')
  rescue StandardError => e
    Rails.logger.error "Failed to fetch profile picture for #{phone_number}: #{e.message}"
    nil
  end

  def try_update_contact_avatar
    # TODO: Current logic will never update the contact avatar if their profile picture changes on WhatsApp.
    return if @contact.avatar.attached?

    phone = extract_from_jid(type: 'pn')
    profile_pic_url = fetch_profile_picture_url(phone) if phone
    ::Avatar::AvatarFromUrlJob.perform_later(@contact, profile_pic_url) if profile_pic_url
  end

  def message_under_process?
    key = format(Redis::RedisKeys::MESSAGE_SOURCE_KEY, id: "#{inbox.id}_#{raw_message_id}")
    Redis::Alfred.get(key)
  end

  def cache_message_source_id_in_redis
    key = format(Redis::RedisKeys::MESSAGE_SOURCE_KEY, id: "#{inbox.id}_#{raw_message_id}")
    ::Redis::Alfred.setex(key, true)
  end

  def clear_message_source_id_from_redis
    key = format(Redis::RedisKeys::MESSAGE_SOURCE_KEY, id: "#{inbox.id}_#{raw_message_id}")
    ::Redis::Alfred.delete(key)
  end
end
