# frozen_string_literal: true

class EventNormalizer
  class NormalizationError < StandardError; end

  # Normalize an event by populating all the normalized fields
  def self.normalize_event!(event)
    normalizer = new(event)
    normalizer.normalize!
    event
  end

  def initialize(event)
    @event = event
  end

  def normalize!
    return unless @event.present?

    normalize_host
    normalize_action
    normalize_method
    normalize_protocol
    normalize_path_segments
  end

  private

  def normalize_host
    hostname = extract_hostname
    return unless hostname

    host = RequestHost.find_or_create_host(hostname)
    # NOTE: usage_count increment removed for performance (was adding ~50ms per event)
    # Can be recalculated with: RequestHost.all.each { |h| h.update(usage_count: h.events.count) }
    @event.request_host = host
  end

  def normalize_action
    raw_action = @event.instance_variable_get(:@raw_action)
    return unless raw_action.present?

    action_enum = case raw_action.to_s.downcase
                  when 'deny', 'block' then :deny
                  when 'allow', 'pass' then :allow
                  when 'redirect' then :redirect
                  when 'challenge' then :challenge
                  when 'log', 'monitor' then :log
                  else :allow
                  end

    @event.waf_action = action_enum
  end

  def normalize_method
    raw_method = @event.instance_variable_get(:@raw_request_method)
    return unless raw_method.present?

    method_enum = case raw_method.to_s.upcase
                  when 'GET' then :get
                  when 'POST' then :post
                  when 'PUT' then :put
                  when 'PATCH' then :patch
                  when 'DELETE' then :delete
                  when 'HEAD' then :head
                  when 'OPTIONS' then :options
                  else :get
                  end

    @event.request_method = method_enum
  end

  def normalize_protocol
    raw_protocol = @event.instance_variable_get(:@raw_request_protocol)
    return unless raw_protocol.present?

    # Store the protocol directly (HTTP/1.1, HTTP/2, etc.)
    # Could normalize to enum if needed, but keeping as string for flexibility
    @event.request_protocol = raw_protocol
  end

  def normalize_path_segments
    segments = @event.path_segments_array
    return if segments.empty?

    segment_ids = segments.map do |segment|
      path_segment = PathSegment.find_or_create_segment(segment)
      # NOTE: usage_count increment removed for performance (was adding ~100ms per event for paths with many segments)
      # Can be recalculated with: PathSegment.all.each { |ps| ps.update(usage_count: Event.where("request_segment_ids @> ARRAY[?]", ps.id).count) }
      path_segment.id
    end

    @event.request_segment_ids = segment_ids
  end

  def extract_hostname
    # Try to extract hostname from various sources
    return @event.request_hostname if @event.respond_to?(:request_hostname)

    # Extract from request URL if available
    if @event.request_url.present?
      begin
        uri = URI.parse(@event.request_url)
        return uri.hostname
      rescue URI::InvalidURIError
        nil
      end
    end

    # Extract from payload as fallback
    if @event.payload.present?
      url = @event.payload.dig("request", "url")
      if url.present?
        begin
          uri = URI.parse(url)
          return uri.hostname
        rescue URI::InvalidURIError
          nil
        end
      end
    end

    nil
  end
end