# frozen_string_literal: true

module Telephony
  class OtpSender
    attr_reader :recipient_phone, :otp, :expiration, :otp_format, :otp_length, :channel,
                :domain, :country_code, :extra_metadata

    def initialize(to:, otp:, expiration:, otp_format:,
                   channel:, domain:, country_code:, extra_metadata:,
                   otp_length: I18n.t('telephony.format_length.six'))
      @recipient_phone = to
      @otp = otp
      @otp_format = otp_format
      @otp_length = otp_length
      @expiration = expiration
      @channel = channel.to_sym
      @domain = domain
      @country_code = country_code
      @extra_metadata = extra_metadata
    end

    def send_authentication_otp
      response = adapter.deliver(
        message: authentication_message,
        to: recipient_phone,
        otp: otp,
        country_code: country_code,
      )
      log_response(response, context: :authentication)
      response
    end

    def send_confirmation_otp
      response = adapter.deliver(
        message: confirmation_message,
        to: recipient_phone,
        otp: otp,
        country_code: country_code,
      )
      log_response(response, context: :confirmation)
      response
    end

    def authentication_message
      wrap_in_ssml_if_needed(
        I18n.t(
          "telephony.authentication_otp.#{channel}",
          app_name: APP_NAME,
          code: otp_transformed_for_channel,
          format_type: otp_format,
          expiration: expiration,
          domain: domain,
        ),
      )
    end

    def confirmation_message
      wrap_in_ssml_if_needed(
        I18n.t(
          "telephony.confirmation_otp.#{channel}",
          app_name: APP_NAME,
          code: otp_transformed_for_channel,
          format_length: otp_length,
          format_type: otp_format,
          expiration: expiration,
          domain: domain,
        ),
      )
    end

    private

    def adapter
      case [Telephony.config.adapter, channel.to_sym]
      when [:pinpoint, :sms]
        Pinpoint::SmsSender.new
      when [:pinpoint, :voice]
        Pinpoint::VoiceSender.new
      when [:test, :sms]
        Test::SmsSender.new
      when [:test, :voice]
        Test::VoiceSender.new
      else
        raise "Unknown telephony adapter #{Telephony.config.adapter} for channel #{channel.to_sym}"
      end
    end

    def log_response(response, context:)
      extra = extra_metadata.merge(
        {
          adapter: Telephony.config.adapter,
          channel: channel,
          context: context,
          country_code: country_code,
        },
      )
      output = response.to_h.merge(extra)
      Telephony.log_info(event: output)
    end

    def otp_transformed_for_channel
      return otp if channel != :voice

      [*otp.chars, ''].join(" <break time='#{Telephony.config.voice_pause_time}' /> ")
    end

    def wrap_in_ssml_if_needed(message)
      return message if channel != :voice

      <<~XML.squish
        <speak>
          <prosody rate='#{Telephony.config.voice_rate}'>
            #{message}
          </prosody>
        </speak>
      XML
    end
  end
end
