defmodule Towerops.Admin.AuditLogger do
  @moduledoc """
  Helper module for creating audit log entries with automatic extraction of
  request metadata (IP address, user agent, request path).

  This module provides convenience functions for logging various types of
  audit events throughout the application.
  """

  alias Towerops.Admin

  @doc """
  Logs an audit event with automatic extraction of conn metadata.

  ## Parameters

    * `action` - String representing the action being logged
    * `conn` - The Plug.Conn struct (can be nil for background jobs)
    * `opts` - Keyword list of options:
      * `:actor_id` - ID of the user performing the action (optional)
      * `:target_user_id` - ID of the user being acted upon (optional)
      * `:metadata` - Additional metadata map (optional)
      * `:data_accessed` - Map of data fields that were accessed (optional)

  ## Examples

      iex> log_event("user_data_viewed", conn, actor_id: admin_id, target_user_id: user_id)
      {:ok, %AuditLog{}}
  """
  def log_event(action, conn \\ nil, opts \\ []) do
    attrs = build_attrs(action, conn, opts)
    Admin.create_audit_log(attrs)
  end

  @doc """
  Logs when a user's data is viewed by an admin or another user.
  """
  def log_user_data_viewed(conn, viewer_id, target_user_id, data_accessed) do
    log_event("user_data_viewed", conn,
      actor_id: viewer_id,
      target_user_id: target_user_id,
      data_accessed: data_accessed
    )
  end

  @doc """
  Logs when a user exports their data.
  """
  def log_user_data_exported(conn, user_id) do
    log_event("user_data_exported", conn,
      actor_id: user_id,
      target_user_id: user_id,
      metadata: %{export_format: "json"}
    )
  end

  @doc """
  Logs when a user's profile is updated.
  """
  def log_user_profile_updated(conn, user_id, fields_changed) do
    log_event("user_profile_updated", conn,
      actor_id: user_id,
      target_user_id: user_id,
      data_accessed: %{fields_changed: fields_changed}
    )
  end

  @doc """
  Logs when organization data is accessed.
  """
  def log_org_data_accessed(conn, user_id, org_id, action_detail) do
    log_event("org_data_accessed", conn,
      actor_id: user_id,
      metadata: %{organization_id: org_id, action: action_detail}
    )
  end

  @doc """
  Logs device creation.
  """
  def log_device_created(conn, user_id, device_id, device_name) do
    log_event("device_created", conn,
      actor_id: user_id,
      metadata: %{device_id: device_id, device_name: device_name}
    )
  end

  @doc """
  Logs device update.
  """
  def log_device_updated(conn, user_id, device_id, fields_changed) do
    log_event("device_updated", conn,
      actor_id: user_id,
      metadata: %{device_id: device_id},
      data_accessed: %{fields_changed: fields_changed}
    )
  end

  @doc """
  Logs device deletion.
  """
  def log_device_deleted(conn, user_id, device_id, device_name) do
    log_event("device_deleted", conn,
      actor_id: user_id,
      metadata: %{device_id: device_id, device_name: device_name}
    )
  end

  @doc """
  Logs when monitoring data is queried.
  """
  def log_monitoring_data_queried(conn, user_id, device_id, time_range) do
    log_event("monitoring_data_queried", conn,
      actor_id: user_id,
      metadata: %{device_id: device_id, time_range: time_range}
    )
  end

  @doc """
  Logs failed access attempts.
  """
  def log_failed_access_attempt(conn, user_id, resource, reason) do
    log_event("failed_access_attempt", conn,
      actor_id: user_id,
      metadata: %{resource: resource, reason: reason}
    )
  end

  @doc """
  Logs privilege escalations (e.g., becoming org admin).
  """
  def log_privilege_escalation(conn, user_id, from_role, to_role, context) do
    log_event("privilege_escalation", conn,
      actor_id: user_id,
      metadata: %{from_role: from_role, to_role: to_role, context: context}
    )
  end

  # Private helper functions

  defp build_attrs(action, conn, opts) do
    base_attrs = %{
      action: action,
      superuser_id: Keyword.get(opts, :actor_id),
      target_user_id: Keyword.get(opts, :target_user_id),
      metadata: Keyword.get(opts, :metadata, %{}),
      data_accessed: Keyword.get(opts, :data_accessed)
    }

    if conn do
      base_attrs
      |> Map.put(:ip_address, get_ip_address(conn))
      |> Map.put(:user_agent, get_user_agent(conn))
      |> Map.put(:request_path, get_request_path(conn))
    else
      base_attrs
    end
  end

  defp get_ip_address(%Plug.Conn{} = conn) do
    case Plug.Conn.get_req_header(conn, "x-forwarded-for") do
      [ip | _] -> ip |> String.split(",") |> List.first() |> String.trim()
      [] -> to_string(:inet_parse.ntoa(conn.remote_ip))
    end
  end

  defp get_user_agent(%Plug.Conn{} = conn) do
    case Plug.Conn.get_req_header(conn, "user-agent") do
      [ua | _] -> ua
      [] -> nil
    end
  end

  defp get_request_path(%Plug.Conn{} = conn) do
    conn.request_path
  end
end
