defmodule Towerops.Security.FourOhFourTracker do
  @moduledoc """
  Tracks unique 404 paths per IP address using Redis.

  Uses Redis SET data structure to track unique paths with 60-second TTL.
  When an IP hits 5+ unique 404s, a ban is created/escalated.
  """

  alias Towerops.Security.BruteForce

  require Logger

  @threshold 5
  @window_seconds 60

  @doc """
  Records a 404 path for an IP address.

  Stores the path in a Redis SET with 60-second TTL.
  Triggers ban creation/escalation if threshold (5) is reached.
  """
  def record_404(ip_address, path) do
    key = redis_key(ip_address)
    conn = Towerops.Redix

    try do
      # Add path to set
      Redix.command(conn, ["SADD", key, path])
      # Set TTL if this is the first path
      Redix.command(conn, ["EXPIRE", key, @window_seconds])

      # Get unique count
      case Redix.command(conn, ["SCARD", key]) do
        {:ok, count} when count >= @threshold ->
          Logger.warning("IP #{ip_address} hit threshold with #{count} unique 404s, triggering ban")
          BruteForce.create_or_escalate_ban(ip_address)
          :threshold_reached

        {:ok, count} ->
          Logger.debug("IP #{ip_address} has #{count}/#{@threshold} unique 404s")
          :ok

        {:error, reason} ->
          Logger.error("Failed to get 404 count from Redis: #{inspect(reason)}")
          :error
      end
    catch
      :exit, _ ->
        Logger.error("Redis unavailable for 404 tracking")
        :error
    end
  end

  @doc """
  Gets the count of unique 404 paths for an IP address.
  """
  def get_count(ip_address) do
    key = redis_key(ip_address)
    conn = Towerops.Redix

    try do
      case Redix.command(conn, ["SCARD", key]) do
        {:ok, count} -> count
        {:error, _} -> 0
      end
    catch
      :exit, _ -> 0
    end
  end

  defp redis_key(ip_address) do
    "404_scan:#{ip_address}"
  end
end
