class SlackSmartBot
  def repl_client(team_id_user, session_name, type, serialt, env_vars)
    message = "Session name: *#{session_name}*
        From now on I will execute all you write as a Ruby command and I will keep the session open until you send `quit` or `bye` or `exit`.
        In case you need someone to help you with the session you can add collaborators by sending `add collaborator @USER` to the session.
        I will respond with the result so it is not necessary you send `print`, `puts`, `p` or `pp` unless you want it as the output when calling `run repl`.
        Use `p` to print a message raw, exacly like it is returned.
        If you want to avoid a message to be treated by me, start the message with '-'.
        After 30 minutes of no communication with the Smart Bot the session will be dismissed.
        If you want to see the methods of a class or module you created use _ls TheModuleOrClass_
        To see the code of a method: _code TheModuleOrClass.my_method_. To see the documentation of a method: _doc TheModuleOrClass.my_method_
        You can ask *ChatGPT* to help you or suggest any code by sending the message: `? PROMPT`. If no prompt then it will suggest the next line of code.
        You can supply the Environmental Variables you need for the Session
        Example:
          _repl CreateCustomer LOCATION=spain HOST='https://10.30.40.50:8887'_
        "
    respond message

    File.write("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[team_id_user][:name]}.input", "", mode: "a+")
    File.write("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[team_id_user][:name]}.output", "", mode: "a+")
    File.write("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[team_id_user][:name]}.run", "", mode: "a+")

    if type != :private_clean and type != :public_clean
      pre_execute = '
            if File.exist?(\"./.smart-bot-repl\")
              begin
                eval(File.read(\"./.smart-bot-repl\"), bindme' + serialt + ")
              rescue Exception => resp_repl
              end
            end
          "
    else
      pre_execute = ""
    end

    process_to_run = "
            " + env_vars.join("\n") + '
            require \"amazing_print\"
            require \"stringio\"
            require \"method_source\"
            bindme' + serialt + ' = binding
            eval(\"require \'nice_http\'\" , bindme' + serialt + ')
            def get_met_params(obj, m=nil)
              result = ""
              if m.nil?
                met = obj
              else
                met = obj.method(m)
              end
              met.source.split("\n").each { |line|
                line.gsub!("def", "")
                line.gsub!("self.", "")
                line.strip!
                line.gsub!(/\A(\w+)\(/, \'*`\1`* (\')
                line.gsub!(/\A(\w+)$/, \'*`\1`*\')
                if line.strip[-1] == ")" or (result.empty? and !line.include?("("))
                  line.gsub!(/\A(\w+)\s/, \'*`\1`* \') if !line.include?("(")
                  result << "#{line}"
                  result << " ..." if !line.include?("(") and !line.include?(")")
                  result << "\n"
                  break
                else
                  result << "#{line}\n"
                end
              }
              result
            end
            def ls(obj)
              result = ""
              (obj.methods - Object.methods).sort.each do |m|
                if obj.respond_to?(m)
                  pre = "*`#{obj}`*."
                else
                  pre = ""
                end
                result << "#{pre}#{get_met_params(obj, m)}"
                result << "\n"
              end
              puts result
            end
            def get_object(obj_txt)
              met = obj_txt.scan(/\.(\w+)/).flatten.last

              if met.nil? and obj_txt[0].match(/[A-Z]/)
                obj = Object
                obj_txt.split("::").each do |cl|
                  if obj.const_defined?(cl.to_sym)
                    obj = obj.const_get(cl)
                  else
                    obj = nil
                    break
                  end
                end
              elsif met.nil? and obj_txt[0].match(/[a-z]/)
                begin
                  obj = self.method(obj_txt)
                rescue
                  obj = nil
                end
              else
                cl = obj_txt.scan(/([\w\:]+)\./).flatten.first
                obj = Object
                cl.split("::").each do |cl|
                  if obj.const_defined?(cl.to_sym)
                    obj = obj.const_get(cl)
                  else
                    obj = nil
                    break
                  end
                end
                unless obj.nil?
                  if obj.respond_to?(met)
                    obj = obj.method(met)
                  elsif obj.instance_method(met)
                    obj = obj.instance_method(met)
                  else
                    obj = nil
                  end
                end

              end
            end

            def doc(obj_txt)
              obj = get_object(obj_txt)
              if !obj.nil? and obj.respond_to?(:source_location) and obj.respond_to?(:comment) and
                !obj.source_location.nil? and !obj.comment.nil?
                result = "_*#{obj.source_location.join(":").gsub(Dir.pwd,"").gsub(Dir.home,"")}*_\n\n"
                comment = obj.comment.gsub(/^\s*\#/, "")
                comment.gsub!(/^\s*#+\s*(\R|$)/, "")
                comment.gsub!(/^\s(\w)([\w\s\-]*):/i, \'*\1\2*:\')
                comment.gsub!(/^(\s+)(\w[\w\s\-]*):/i, \'\1*`\2`*:\')
                result << "#{comment}"
                result << "\n\n"
                result << get_met_params(obj)
              else
                result = "No documentation found for #{obj_txt}. The object doesn\'t exist or it is not accessible."
              end
              puts result
            end

            def source(obj_txt)
              obj = get_object(obj_txt)
              if !obj.nil? and obj.respond_to?(:source_location) and obj.respond_to?(:comment) and
                !obj.source_location.nil? and !obj.comment.nil?
                result = "# #{obj.source_location.join(":").gsub(Dir.pwd,"").gsub(Dir.home,"")}\n\n"
                result << "#{obj.source}"
              else
                result = "No source code found for #{obj_txt}. The object doesn\'t exist or it is not accessible."
              end
              puts result
            end

            def code(obj_txt)
              source(obj_txt)
            end

            file_run_path = \"' + +File.expand_path(config.path) + "/repl/" + @channel_id + "/" + session_name + '.rb\"
            file_input_repl = File.open(\"' + File.expand_path(config.path) + "/repl/" + @channel_id + "/" + session_name + '.input\", \"r\")
            ' + pre_execute + '
            while true do
              sleep 0.2
              code_to_run_repl = file_input_repl.read
              if code_to_run_repl.to_s!=\"\"
                add_to_run_repl = true
                if code_to_run_repl.to_s.match?(/^quit$/i) or
                  code_to_run_repl.to_s.match?(/^exit$/i) or
                  code_to_run_repl.to_s.match?(/^bye bot$/i) or
                  code_to_run_repl.to_s.match?(/^bye$/i)
                  exit
                else
                  error = false
                  as_it_is = false
                  begin
                      if code_to_run_repl.match?(/^\s*ls\s+(.+)/)
                        add_to_run_repl = false
                        as_it_is = true
                      elsif code_to_run_repl.match(/^\s*doc\s+(.+)/)
                        add_to_run_repl = false
                        code_to_run_repl = \"doc \\\"#{$1}\\\"\"
                        as_it_is = true
                      elsif code_to_run_repl.match(/^\s*(code|source|src)\s+(.+)/)
                        add_to_run_repl = false
                        code_to_run_repl = \"source \\\"#{$2}\\\"\"
                      end
                    begin
                      original_stdout = $stdout
                      $stdout = StringIO.new
                      resp_repl = eval(code_to_run_repl, bindme' + serialt + ')
                      stdout_repl = $stdout.string
                    ensure
                      $stdout = original_stdout
                    end
                  rescue Exception => resp_repl
                    error = true
                  end
                  if error
                    open(\"' + File.expand_path(config.path) + "/repl/" + @channel_id + "/" + session_name + '.output\", \"a+\") {|f|
                      f.puts \"\`\`\`\n#{resp_repl.to_s.gsub(/^.+' + session_name + '\.rb:\d+:/,\"\")}\`\`\`\"
                    }
                  else
                    if code_to_run_repl.match?(/^\s*p\s+/i)
                      resp_repl = stdout_repl unless stdout_repl.to_s == \'\'
                      if stdout_repl.to_s == \'\'
                        resp_repl = resp_repl.inspect
                      else
                        resp_repl = stdout_repl
                      end
                      open(\"' + File.expand_path(config.path) + "/repl/" + @channel_id + "/" + session_name + '.output\", \"a+\") {|f|
                        f.puts \"\`\`\`\n#{resp_repl}\`\`\`\"
                      }
                    else
                      if stdout_repl.to_s == \'\'
                        resp_repl = resp_repl.ai
                      else
                        resp_repl = stdout_repl
                      end
                      open(\"' + File.expand_path(config.path) + "/repl/" + @channel_id + "/" + session_name + '.output\", \"a+\") {|f|
                        if as_it_is
                          f.puts resp_repl
                        else
                          f.puts \"\`\`\`\n#{resp_repl}\`\`\`\"
                        end
                      }
                    end
                    unless !add_to_run_repl
                      open(\"' + File.expand_path(config.path) + "/repl/" + @channel_id + "/" + session_name + '.run\", \"a+\") {|f|
                        f.puts code_to_run_repl
                      }
                    end
                  end
                end
              end
            end
        '
    process_to_run.gsub!('\"', '"')
    return process_to_run
  end
end