# frozen_string_literal: true

class Api::EventsController < ApplicationController
  skip_before_action :verify_authenticity_token
  allow_unauthenticated_access # Skip normal session auth, use DSN auth instead

  # POST /api/events
  def create
    dsn = authenticate_dsn!
    return head :not_found unless dsn

    # Parse the incoming WAF event data
    event_data = parse_event_data(request)

    # Create event asynchronously
    ProcessWafEventJob.perform_later(
      event_data: event_data,
      headers: extract_serializable_headers(request)
    )

    # Include rule version in response for agent optimization
    rule_version = Rule.latest_version
    response.headers['X-Rule-Version'] = rule_version.to_s

    # Get current sampling for back-pressure management
    current_sampling = HubLoad.current_sampling
    response.headers['X-Sample-Rate'] = current_sampling[:allowed_requests].to_s
    response.headers['X-Sample-Until'] = current_sampling[:effective_until]

    # Check if agent sent a rule version in the JSON body to compare against
    client_version = event_data.dig('last_rule_sync')&.to_i

    response_data = {
      success: true,
      rule_version: rule_version,
      sampling: current_sampling
    }

    # If agent has old rules or no version, include new rules in response
    if client_version.blank? || client_version != rule_version
      # Get rules updated since client version
      if client_version.present?
        rules = Rule.since(client_version).enabled
      else
        # Full sync for new agents
        rules = Rule.active.sync_order
      end

      agent_rules = rules.map(&:to_agent_format)
      response_data[:rules] = agent_rules
      response_data[:rules_changed] = true

      # Include path segments dictionary for path_pattern rules
      path_segment_ids = agent_rules
        .select { |r| r[:waf_rule_type] == 'path_pattern' }
        .flat_map { |r| r.dig(:conditions, :segment_ids) }
        .compact
        .uniq

      if path_segment_ids.any?
        response_data[:path_segments] = PathSegment
          .where(id: path_segment_ids)
          .pluck(:id, :segment)
          .to_h
      end
    else
      response_data[:rules_changed] = false
    end

    render json: response_data
  rescue DsnAuthenticationService::AuthenticationError => e
    Rails.logger.warn "DSN authentication failed: #{e.message}"
    head :unauthorized
  rescue JSON::ParserError => e
    Rails.logger.error "Invalid JSON in event data: #{e.message}"
    head :bad_request
  end

  private

  def authenticate_dsn!
    DsnAuthenticationService.authenticate(request)
  end

  def parse_event_data(request)
    # Handle different content types
    content_type = request.content_type || "application/json"

    case content_type
    when /application\/json/
      JSON.parse(request.body.read)
    when /application\/x-www-form-urlencoded/
      # Convert form data to JSON-like hash
      request.request_parameters
    else
      # Try to parse as JSON anyway
      JSON.parse(request.body.read)
    end
  rescue => e
    Rails.logger.error "Failed to parse event data: #{e.message}"
    {}
  ensure
    request.body.rewind if request.body.respond_to?(:rewind)
  end

  def extract_serializable_headers(request)
    # Only extract the headers we need for analytics, avoiding IO objects
    important_headers = %w[
      User-Agent Content-Type Content-Length Accept
      X-Forwarded-For X-Real-IP X-Forwarded-Proto
      Authorization X-Baffle-Auth X-Sentry-Auth
      Referer Accept-Language Accept-Encoding
    ]

    headers = {}
    important_headers.each do |header|
      value = request.headers[header]
      # Standardize headers to lower case during import phase
      headers[header.downcase] = value if value.present?
    end

    headers
  end
end