# coding: utf-8
# frozen_string_literal: true

module Stealth
  class Controller
    module Replies

      extend ActiveSupport::Concern

      included do

        class_attribute :_preprocessors, default: [:erb]
        class_attribute :_replies_path, default: [Stealth.root, 'bot', 'replies']

        def send_replies
          yaml_reply, preprocessor = action_replies

          service_reply = Stealth::ServiceReply.new(
            recipient_id: current_session_id,
            yaml_reply: yaml_reply,
            preprocessor: preprocessor,
            context: binding
          )

          service_reply.replies.each_with_index do |reply, i|
            handler = reply_handler.new(
              recipient_id: current_message.sender_id,
              reply: reply
            )

            translated_reply = handler.send(reply.reply_type)
            client = service_client.new(reply: translated_reply)
            client.transmit

            # If this was a 'delay' type of reply, we insert the delay
            if reply.reply_type == 'delay'
              begin
                if reply['duration'] == 'dynamic'
                  m = Stealth.config.dynamic_delay_muliplier
                  duration = dynamic_delay(
                    service_replies: service_reply.replies,
                    position: i
                  )

                  sleep_duration = Stealth.config.dynamic_delay_muliplier * duration
                else
                  sleep_duration = Float(reply['duration'])
                end

                sleep(sleep_duration)
              rescue ArgumentError, TypeError
                raise(ArgumentError, 'Invalid duration specified. Duration must be a float')
              end
            end
          end

          @progressed = :sent_replies
        end

        private

          def service_client
            begin
              Kernel.const_get("Stealth::Services::#{current_service.classify}::Client")
            rescue NameError
              raise(Stealth::Errors::ServiceNotRecognized, "The service '#{current_service}' was not recognized")
            end
          end

          def reply_handler
            begin
              Kernel.const_get("Stealth::Services::#{current_service.classify}::ReplyHandler")
            rescue NameError
              raise(Stealth::Errors::ServiceNotRecognized, "The service '#{current_service}' was not recognized")
            end
          end

          def replies_folder
            current_session.flow_string.underscore.pluralize
          end

          def reply_dir
            [*self._replies_path, replies_folder]
          end

          def base_reply_filename
            "#{current_session.state_string}.yml"
          end

          def reply_filenames
            service_filename = [base_reply_filename, current_service].join('+')

            # Service-specific filenames take precedance (returned first)
            [service_filename, base_reply_filename]
          end

          def find_reply_and_preprocessor
            selected_preprocessor = :none
            reply_file_path = File.join(*reply_dir, base_reply_filename)
            service_reply_path = File.join(*reply_dir, reply_filenames.first)

            # Check if the service_filename exists
            # If so, we can skip checking for a preprocessor
            if File.exist?(service_reply_path)
              return service_reply_path, selected_preprocessor
            end

            # Cycles through possible preprocessor and variant combinations
            # Early returns for performance
            for preprocessor in self.class._preprocessors do
              for reply_filename in reply_filenames do
                selected_filepath = File.join(*reply_dir, [reply_filename, preprocessor.to_s].join('.'))
                if File.exist?(selected_filepath)
                  reply_file_path = selected_filepath
                  selected_preprocessor = preprocessor
                  return reply_file_path, selected_preprocessor
                end
              end
            end

            return reply_file_path, selected_preprocessor
          end

          def action_replies
            reply_file_path, selected_preprocessor = find_reply_and_preprocessor

            begin
              file_contents = File.read(reply_file_path)
            rescue Errno::ENOENT
              raise(Stealth::Errors::ReplyNotFound, "Could not find a reply in #{reply_dir}")
            end

            return file_contents, selected_preprocessor
          end

      end # instance methods

    end
  end
end