defmodule Aprsme.LogSanitizer do
  @moduledoc """
  Utility module for sanitizing log output to prevent sensitive data leakage
  """

  @sensitive_fields [
    :password,
    :passcode,
    :auth_token,
    :api_key,
    :secret,
    :token,
    :authorization,
    :csrf_token,
    :session_token,
    :private_key,
    :database_url,
    :secret_key_base,
    :signing_salt
  ]

  @sensitive_patterns [
    ~r/password[=:]\s*\S+/i,
    ~r/passcode[=:]\s*\S+/i,
    ~r/token[=:]\s*\S+/i,
    ~r/key[=:]\s*\S+/i,
    ~r/secret[=:]\s*\S+/i,
    ~r/authorization[=:]\s*\S+/i
  ]

  @doc """
  Sanitize a map by removing or redacting sensitive fields
  """
  def sanitize_map(data) when is_map(data) do
    Enum.reduce(@sensitive_fields, data, fn field, acc ->
      cond do
        Map.has_key?(acc, field) -> Map.put(acc, field, "[REDACTED]")
        Map.has_key?(acc, to_string(field)) -> Map.put(acc, to_string(field), "[REDACTED]")
        true -> acc
      end
    end)
  end

  def sanitize_map(data), do: data

  @doc """
  Sanitize a string by replacing sensitive patterns
  """
  def sanitize_string(data) when is_binary(data) do
    Enum.reduce(@sensitive_patterns, data, fn pattern, acc ->
      Regex.replace(pattern, acc, fn match ->
        case String.split(match, ["=", ":"]) do
          [key, _value] -> "#{key}=[REDACTED]"
          _ -> "[REDACTED]"
        end
      end)
    end)
  end

  def sanitize_string(data), do: data

  @doc """
  Sanitize any data structure recursively
  """
  def sanitize(data) when is_map(data) do
    data
    |> sanitize_map()
    |> Map.new(fn {k, v} -> {k, sanitize(v)} end)
  end

  def sanitize(data) when is_list(data) do
    Enum.map(data, &sanitize/1)
  end

  def sanitize(data) when is_binary(data) do
    sanitize_string(data)
  end

  def sanitize(data), do: data

  @doc """
  Sanitize packet data for logging
  """
  def sanitize_packet_data(packet_data) when is_map(packet_data) do
    # Remove potentially sensitive packet content but keep structure
    packet_data
    |> Map.drop([:raw_packet, :comment, :status, :data_extended])
    |> sanitize_map()
  end

  def sanitize_packet_data(data), do: data

  @doc """
  Sanitize connection information for logging
  """
  def sanitize_connection_info(conn_info) when is_map(conn_info) do
    conn_info
    |> Map.drop([:password, :passcode])
    |> Map.put(:password, "[REDACTED]")
    |> Map.put(:passcode, "[REDACTED]")
  end

  def sanitize_connection_info(data), do: data

  @doc """
  Sanitize error information for logging
  """
  def sanitize_error_context(context) when is_map(context) do
    context
    |> sanitize_map()
    |> Map.update(:stacktrace, nil, fn stacktrace ->
      if is_list(stacktrace) do
        # Limit stacktrace length
        Enum.take(stacktrace, 5)
      else
        stacktrace
      end
    end)
  end

  def sanitize_error_context(data), do: data

  @doc """
  Create a sanitized logging keyword list
  """
  def log_data(keyword_list) when is_list(keyword_list) do
    Enum.map(keyword_list, fn {key, value} ->
      {key, sanitize(value)}
    end)
  end

  def log_data(data), do: sanitize(data)
end
