ruby_version_is ""..."4.1" do
  module NetFTPSpecs
    class DummyFTP
      attr_accessor :connect_message
      attr_reader :login_user, :login_pass, :login_acct

      # hostname or IP address
      attr_reader :hostname
      # port number
      attr_reader :server_port

      def initialize
        @hostname = "127.0.0.1"
        @server = TCPServer.new(@hostname, 0)
        @server_port = @server.addr[1]

        @handlers = {}
        @commands = []
        @connect_message = nil
      end

      def serve_once
        @thread = Thread.new do
          @socket = @server.accept
          @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, 1)
          begin
            handle_request
          ensure
            @socket.close
          end
        end
      end

      def handle_request
        # Send out the welcome message.
        response @connect_message || "220 Dummy FTP Server ready!"

        begin
          while command = @socket.gets
            command, argument = command.chomp.split(" ", 2)

            if command == "QUIT"
              self.response("221 OK, bye")
              break
            elsif proc_handler = @handlers[command.downcase.to_sym]
              if argument.nil?
                proc_handler.call(self)
              else
                proc_handler.call(self, argument)
              end
            else
              if argument.nil?
                self.send(command.downcase.to_sym)
              else
                self.send(command.downcase.to_sym, argument)
              end
            end
          end
        rescue => e
          self.error_response("Exception: #{e} #{e.backtrace.inspect}")
        end
      end

      def error_response(text)
        self.response("451 #{text}")
      end

      def response(text)
        @socket.puts(text) unless @socket.closed?
      end

      def stop
        @datasocket.close unless @datasocket.nil? || @datasocket.closed?
        @server.close
        @thread.join
      end


      ##
      def handle(sym, &block)
        @handlers[sym] = block
      end

      def should_receive(method)
        @handler_for = method
        self
      end

      def and_respond(text)
        @handlers[@handler_for] = -> s, *args { s.response(text) }
      end

      ##
      # FTP methods
      ##

      def abor
        self.response("226 Closing data connection. (ABOR)")
      end

      def acct(account)
        @login_acct = account
        self.response("230 User '#{account}' logged in, proceed. (ACCT)")
      end

      def cdup
        self.response("200 Command okay. (CDUP)")
      end

      def cwd(dir)
        self.response("200 Command okay. (CWD #{dir})")
      end

      def dele(file)
        self.response("250 Requested file action okay, completed. (DELE #{file})")
      end

      def eprt(arg)
        _, _, host, port = arg.split("|")

        @datasocket = TCPSocket.new(host, port)
        self.response("200 port opened")
      end

      def help(param = :default)
        if param == :default
          self.response("211 System status, or system help reply. (HELP)")
        else
          self.response("211 System status, or system help reply. (HELP #{param})")
        end
      end

      def list(folder)
        self.response("150 opening ASCII connection for file list")
        @datasocket.puts("-rw-r--r--  1 spec  staff  507 17 Jul 18:41 last_response_code.rb")
        @datasocket.puts("-rw-r--r--  1 spec  staff   50 17 Jul 18:41 list.rb")
        @datasocket.puts("-rw-r--r--  1 spec  staff   48 17 Jul 18:41 pwd.rb")
        @datasocket.close()
        self.response("226 transfer complete (LIST #{folder})")
      end

      def mdtm(filename)
        self.response("213 19980705132316")
      end

      def mkd(foldername)
        self.response(%Q{257 "#{foldername.gsub('"', '""')}" created.})
      end

      def nlst(folder = nil)
        self.response("150 opening ASCII connection for file list")
        @datasocket.puts("last_response_code.rb")
        @datasocket.puts("list.rb")
        @datasocket.puts("pwd.rb")
        @datasocket.close()
        self.response("226 transfer complete (NLST#{folder ? " #{folder}" : ""})")
      end

      def noop
        self.response("200 Command okay. (NOOP)")
      end

      def pass(password)
        @login_pass = password
        self.response("230 User logged in, proceed. (PASS #{password})")
      end

      def port(arg)
        nums = arg.split(",")

        if nums[0] == "::1"
          # IPv6
          port = nums[1].to_i * 256 + nums[2].to_i
          host = nums[0]
        else
          # IPv4
          port = nums[4].to_i * 256 + nums[5].to_i
          host = nums[0..3].join(".")
        end

        @datasocket = TCPSocket.new(host, port)
        self.response("200 port opened")
      end

      def pwd
        self.response('257 "/some/dir/" - current directory')
      end

      def retr(file)
        self.response("125 Data transfer starting")
        if @restart_at && @restart_at == 20
          @datasocket.puts("of the file named '#{file}'.")
          @restart_at = nil
        else
          @datasocket.puts("This is the content")
          @datasocket.puts("of the file named '#{file}'.")
        end
        @datasocket.close()
        self.response("226 Closing data connection. (RETR #{file})")
      end

      def rest(at_bytes)
        @restart_at = at_bytes.to_i
        self.response("350 Requested file action pending further information. (REST)")
      end

      def rmd(folder)
        self.response("250 Requested file action okay, completed. (RMD #{folder})")
      end

      def rnfr(from)
        @rename_from = from
        self.response("350 Requested file action pending further information.")
      end

      def rnto(to)
        self.response("250 Requested file action okay, completed. (Renamed #{@rename_from} to #{to})")
        @rename_from = nil
      end

      def site(param)
        self.response("200 Command okay. (SITE #{param})")
      end

      def size(filename)
        if filename == "binary"
          self.response("213 24")
        else
          self.response("213 1024")
        end
      end

      def stat(param = :default)
        if param == :default
          self.response("211 System status, or system help reply. (STAT)")
        else
          self.response("211 System status, or system help reply. (STAT #{param})")
        end
      end

      def stor(file)
        tmp_file = tmp("#{file}file", false)

        self.response("125 Data transfer starting.")

        mode = @restart_at ? "a" : "w"

        File.open(tmp_file, mode + "b") do |f|
          loop do
            data = @datasocket.recv(1024)
            break if !data || data.empty?
            f << data
          end
        end

        @datasocket.close()
        self.response("200 OK, Data received. (STOR #{file})")
      end

      def appe(file)
        @restart_at = true
        stor(file)
      end

      def syst
        self.response("215 FTP Dummy Server (SYST)")
      end

      def type(type)
        self.response("200 TYPE switched to #{type}")
      end

      def user(name)
        @login_user = name
        self.response("230 User logged in, proceed. (USER #{name})")
      end
    end
  end
end
