# frozen_string_literal: true

# PathScannerDetectorJob - Detects IPs hitting scanner paths and auto-bans them
#
# This job analyzes recent events to find IPs hitting common scanner/bot paths
# like /.env, /.git, /wp-admin, etc. When detected, it creates temporary IP
# block rules that expire after 24 hours.
#
# Schedule: Every 5 minutes (configured in initializer or cron)
class PathScannerDetectorJob < ApplicationJob
  queue_as :default

  # Common paths that scanners/bots hit
  SCANNER_PATHS = %w[
    /.env
    /.git
    /wp-admin
    /wp-login.php
    /phpMyAdmin
    /phpmyadmin
    /.aws
    /.ssh
    /admin
    /administrator
    /.config
    /backup
    /db_backup
    /.DS_Store
    /web.config
  ].freeze

  # Minimum hits to be considered a scanner
  MIN_SCANNER_HITS = 3

  # Look back window
  LOOKBACK_WINDOW = 5.minutes

  # Ban duration
  BAN_DURATION = 24.hours

  def perform
    scanner_ips = detect_scanner_ips

    Rails.logger.info "PathScannerDetectorJob: Found #{scanner_ips.count} scanner IPs"

    scanner_ips.each do |ip_data|
      create_ban_rule(ip_data)
    end

    scanner_ips.count
  end

  private

  def detect_scanner_ips
    # Find IPs that have hit scanner paths multiple times recently
    Event
      .where("timestamp > ?", LOOKBACK_WINDOW.ago)
      .where("request_path IN (?)", SCANNER_PATHS)
      .group(:ip_address)
      .select("ip_address, COUNT(*) as hit_count, GROUP_CONCAT(DISTINCT request_path) as paths")
      .having("COUNT(*) >= ?", MIN_SCANNER_HITS)
      .map do |event|
        {
          ip: event.ip_address,
          hit_count: event.hit_count,
          paths: event.paths.to_s.split(",")
        }
      end
  end

  def create_ban_rule(ip_data)
    ip = ip_data[:ip]

    # Check if rule already exists for this IP
    existing_rule = Rule.active.network_rules.find_by(
      "conditions ->> 'cidr' = ?", "#{ip}/32"
    )

    if existing_rule
      Rails.logger.info "PathScannerDetectorJob: Rule already exists for #{ip}, skipping"
      return
    end

    # Determine if IPv4 or IPv6
    addr = IPAddr.new(ip)
    rule_type = addr.ipv4? ? "network_v4" : "network_v6"

    # Create the ban rule
    rule = Rule.create!(
      rule_type: rule_type,
      action: "deny",
      conditions: { cidr: "#{ip}/32" },
      priority: 32,
      expires_at: BAN_DURATION.from_now,
      source: "auto:scanner_detected",
      enabled: true,
      metadata: {
        reason: "Scanner detected: hit #{ip_data[:paths].join(', ')}",
        hit_count: ip_data[:hit_count],
        paths: ip_data[:paths],
        detected_at: Time.current.iso8601,
        auto_generated: true
      }
    )

    Rails.logger.info "PathScannerDetectorJob: Created ban rule #{rule.id} for #{ip} (expires: #{rule.expires_at})"

    rule
  rescue IPAddr::InvalidAddressError => e
    Rails.logger.error "PathScannerDetectorJob: Invalid IP address #{ip}: #{e.message}"
    nil
  rescue ActiveRecord::RecordInvalid => e
    Rails.logger.error "PathScannerDetectorJob: Failed to create rule for #{ip}: #{e.message}"
    nil
  end
end
