require 'translate/text_translator'
module Tools
  #
  # Translator service machinery
  #
  class Translator < Service::Base
    # creates instance variables and getter/setter methods
    attributes :from, :to, :text, :translated_text, :auto, :result, :detected

    ### when .valid? is called
    # validate presence of required params
    validates :text, presence: true
    # validate client creation
    validate :client, if: proc { errors.empty? }
    # attempt the translation
    validate :translated, if: proc { errors.empty? }
    ###

    # Default source and destination languages
    # Allow support for only using a single param
    DEFAULTS = { from: "auto", to: "en" }

    # creates the initial class object from controller params
    def self.from_params(params)
      # merge params with defaults when missing or blank
      options = {
        from: params[:from],
        to: params[:to],
        text: params[:text]
      }.merge!(DEFAULTS) do |_key, param, default|
        param.blank? ? default : param
      end

      # if for some reason someone tried to manually set
      # auto to the destination, set it to english
      options[:to] = "en" if params[:to] == "auto"

      return from_attributes(options)
    end

    def map
      Tools::TextTranslator::SUPPORTED_LANGUAGES.invert
    end

    def mapped
      map.map do |name, code|
        if @auto && code == @detected
          name += ' ( Detected )'
        end
        if code == :auto && @auto
          name = 'Detected: ' + Tools::TextTranslator::SUPPORTED_LANGUAGES[@detected]
        end
        [name, code]
      end
    end

    private

    def client
      @client ||= Tools::TextTranslator.new
      # rubocop:disable RescueStandardError
    rescue => e
      # rubocop:enable RescueStandardError
      errors.add(:base, "Something went wrong#{(': ' + e.message) if e.message.present?}")
    end

    # attempts to translate with input, and adds errors to the activemodel error stack
    def translated
      @result = @client.translate(from: from, to: to, text: text)
    rescue RuntimeError => e
      errors.add(:base, "Something went wrong#{(': ' + e.message) if e.message.present?}")
    else
      @result[:errors].map {|err| errors.add(:base, err)} if @result[:errors].present?

      return unless errors.empty?

      from_result = @result[:from]
      to_result = @result[:to]

      @translated_text = to_result[:text]
      @auto = from_result[:auto_detect?]

      @from = from_result[:language_code].downcase
      if @auto
        @detected = @from.to_sym
        @from = :auto
      end
      @to = to_result[:language_code].downcase
    end

  end
end
