# frozen_string_literal: true

require_relative '../errors/endpoint_not_mapped_error'

##
# Module containing methods to filter Strings
module RequestDataFiltering
  VARIABLE_PATTERN = %r{:[^/]+}

  ##
  # Method responsible for extracting information
  # provided by the client like Headers and Body
  def self.parse_request_data(client, routes)
    path, parameters = extract_url_parameters(client.gets.gsub('HTTP/1.1', ''))
    parameters = {} if parameters.nil?

    method_name = sanitize_method_name(path)
    method_name = select_path(method_name, routes, parameters)
    body_first_line, headers = extract_headers(client)
    body = extract_body(client, body_first_line, headers['Content-Length'].to_i)
    [path, method_name, headers, body, parameters]
  end

  def self.select_path(method_name, routes, parameters)
    return method_name if routes.include?(method_name)

    selected_route = nil
    routes.each do |route|
      split_route = route.split('.')
      split_name = method_name.split('.')

      next unless split_route.length == split_name.length
      next unless match_path_with_route(split_name, split_route)

      selected_route = route
      split_route.each_with_index do |var, index|
        parameters[var[1..].to_sym] = split_name[index] if var =~ VARIABLE_PATTERN
      end
      break
    end

    raise EndpointNotMappedError if selected_route.nil?

    selected_route
  end

  def self.match_path_with_route(split_path, split_route)
    split_route.each_with_index do |var, index|
      return false if var != split_path[index] && !var.match?(VARIABLE_PATTERN)
    end

    true
  end

  ##
  # Method responsible for sanitizing the method name
  def self.sanitize_method_name(path)
    path = extract_path(path)
    method_name = path.gsub('/', '.').strip.downcase
    method_name.gsub!(' ', '')
    method_name
  end

  ##
  # Method responsible for extracting the path from URI
  def self.extract_path(path)
    path[0] == '/' ? path[1..].gsub('/', '.') : path.gsub('/', '.')
  end

  ##
  # Method responsible for extracting the headers from request
  def self.extract_headers(client)
    header = client.gets.delete("\n").delete("\r")
    headers = {}
    while header.match(%r{[a-zA-Z0-9\-/*]*: [a-zA-Z0-9\-/*]})
      split_header = header.split(':')
      headers[split_header[0].strip] = split_header[1].strip
      header = client.gets.delete("\n").delete("\r")
    end
    [header, headers]
  end

  ##
  # Method responsible for extracting the body from request
  def self.extract_body(client, body_first_line, content_length)
    body = client.read(content_length)
    body_first_line << body.to_s
  end

  ##
  # Method responsible for extracting the parameters from URI
  def self.extract_url_parameters(http_first_line)
    return http_first_line, nil unless http_first_line =~ /\?/

    path_and_parameters = http_first_line.split('?', 2)
    path = "#{path_and_parameters[0]} "
    parameters_array = path_and_parameters[1].split('&')
    parameters_array.map! do |item|
      split_item = item.split('=')
      { sanitize_parameter_name(split_item[0]) => sanitize_parameter_value(split_item[1]) }
    end
    parameters = {}
    parameters_array.each { |item| parameters.merge!(item) }
    [path, parameters]
  end

  ##
  # Method responsible for sanitizing the parameter name
  def self.sanitize_parameter_name(name)
    name.gsub(/[^\w\s]/, '')
  end

  ##
  # Method responsible for sanitizing the parameter value
  def self.sanitize_parameter_value(value)
    value.gsub(/[^\w\s]/, '')
    value.gsub(/\s/, '')
  end
end