# frozen_string_literal: true

require 'socket'
require 'watirspec/server/app'

module WatirSpec
  class Server
    class << self
      def run!
        return if running?

        Thread.new do
          run_server
          wait_for_connection
        end
      end

      def port
        @port ||= Selenium::WebDriver::PortProber.above(8180)
      end

      def bind
        @bind ||= Selenium::WebDriver::Platform.windows? ? '127.0.0.1' : '0.0.0.0'
      end

      private

      def running?
        defined?(@running) && @running
      end

      def run_server
        server = TCPServer.new(bind, port)
        @running = true
        loop do
          Thread.start(server.accept) { |client| accept_client(client) }
        end
      end

      def wait_for_connection
        socket_poller = Selenium::WebDriver::SocketPoller.new(bind, port, 30)
        return if socket_poller.connected?

        raise 'Cannot start WatirSpec server.'
      end

      def app
        @app ||= App.new
      end

      def accept_client(client)
        start_line = client.gets
        return unless start_line

        _, path = start_line.split
        status, headers, body = app.response(path, read_body(client))
        headers = prepare_headers(headers, body)

        client.write(response(status, headers, body))
      rescue Errno::ECONNRESET
        Watir.logger.warn 'Client reset connection, skipping.', id: [:reset_connection]
      ensure
        client.close
      end

      def read_body(client)
        request_headers = read_headers(client)
        client.read(request_headers['Content-Length'].to_i)
      end

      def read_headers(client)
        headers = {}
        while (line = client.gets.split(' ', 2))
          break if line[0] == ''

          headers[line[0].chop] = line[1].strip
        end

        headers
      end

      def prepare_headers(headers, body)
        headers = default_headers.merge(headers)
        headers['Content-Length'] = body.size.to_s
        headers.to_a.map { |array| array.join(': ') }
      end

      def default_headers
        {
          'Cache-Control' => 'no-cache',
          'Connection' => 'close',
          'Content-Type' => 'text/html; charset=UTF-8',
          'Server' => 'watirspec'
        }
      end

      def response(status, headers, body)
        response = ["HTTP/1.1 #{status}"] + headers
        response = response.join("\r\n")
        response << "\r\n\r\n#{body}\r\n"
      end
    end # class << Server
  end # Server
end # WatirSpec