class Twilio::VoiceWebhookSetupService
  include Rails.application.routes.url_helpers

  pattr_initialize [:channel!]

  HTTP_METHOD = 'POST'.freeze

  # Returns created TwiML App SID on success.
  def perform
    validate_token_credentials!

    app_sid = create_twiml_app!
    configure_number_webhooks!
    app_sid
  end

  private

  def validate_token_credentials!
    # Only validate Account SID + Auth Token
    token_client.incoming_phone_numbers.list(limit: 1)
  rescue StandardError => e
    log_twilio_error('AUTH_VALIDATION_TOKEN', e)
    raise
  end

  def create_twiml_app!
    friendly_name = "Chatwoot Voice #{channel.phone_number}"
    app = api_key_client.applications.create(
      friendly_name: friendly_name,
      voice_url: channel.voice_call_webhook_url,
      voice_method: HTTP_METHOD
    )
    app.sid
  rescue StandardError => e
    log_twilio_error('TWIML_APP_CREATE', e)
    raise
  end

  def configure_number_webhooks!
    numbers = api_key_client.incoming_phone_numbers.list(phone_number: channel.phone_number)
    if numbers.empty?
      Rails.logger.warn "TWILIO_PHONE_NUMBER_NOT_FOUND: #{channel.phone_number}"
      return
    end

    api_key_client
      .incoming_phone_numbers(numbers.first.sid)
      .update(
        voice_url: channel.voice_call_webhook_url,
        voice_method: HTTP_METHOD,
        status_callback: channel.voice_status_webhook_url,
        status_callback_method: HTTP_METHOD
      )
  rescue StandardError => e
    log_twilio_error('NUMBER_WEBHOOKS_UPDATE', e)
    raise
  end

  def api_key_client
    @api_key_client ||= begin
      cfg = channel.provider_config.with_indifferent_access
      ::Twilio::REST::Client.new(cfg[:api_key_sid], cfg[:api_key_secret], cfg[:account_sid])
    end
  end

  def token_client
    @token_client ||= begin
      cfg = channel.provider_config.with_indifferent_access
      ::Twilio::REST::Client.new(cfg[:account_sid], cfg[:auth_token])
    end
  end

  def log_twilio_error(context, error)
    details = build_error_details(context, error)
    add_twilio_specific_details(details, error)

    backtrace = error.backtrace.is_a?(Array) ? error.backtrace.first(5) : []
    Rails.logger.error("TWILIO_VOICE_SETUP_ERROR: #{details} backtrace=#{backtrace}")
  end

  def build_error_details(context, error)
    cfg = channel.provider_config.with_indifferent_access
    {
      context: context,
      phone_number: channel.phone_number,
      account_sid: cfg[:account_sid],
      error_class: error.class.to_s,
      message: error.message
    }
  end

  def add_twilio_specific_details(details, error)
    details[:status_code] = error.status_code if error.respond_to?(:status_code)
    details[:twilio_code] = error.code if error.respond_to?(:code)
    details[:more_info] = error.more_info if error.respond_to?(:more_info)
    details[:details] = error.details if error.respond_to?(:details)
  end
end
