defmodule Towerops.Admin do
  @moduledoc """
  The Admin context for superuser operations.

  Provides functions for managing users, organizations, and audit logging.
  """
  import Ecto.Query

  alias Towerops.Accounts.User
  alias Towerops.Admin.AuditLog
  alias Towerops.Organizations.Organization
  alias Towerops.Repo

  ## User Management

  @doc """
  Lists all users in the system with their organizations and device counts.

  ## Options

    * `:limit` - Maximum number of users to return (default: 100)
    * `:offset` - Number of users to skip (default: 0)

  ## Examples

      iex> list_all_users()
      [%User{}, ...]

      iex> list_all_users(limit: 50, offset: 100)
      [%User{}, ...]
  """
  @spec list_all_users(Keyword.t()) :: [User.t()]
  def list_all_users(opts \\ []) do
    limit = Keyword.get(opts, :limit, 100)
    offset = Keyword.get(opts, :offset, 0)

    # Subquery to count devices across all user's organizations
    device_count_query =
      from d in "devices",
        join: m in "organization_memberships",
        on: d.organization_id == m.organization_id,
        where: m.user_id == parent_as(:user).id,
        select: count(d.id)

    User
    |> from(as: :user)
    |> select_merge([u], %{device_count: subquery(device_count_query)})
    |> order_by([u], desc: u.inserted_at)
    |> limit(^limit)
    |> offset(^offset)
    |> preload(:organizations)
    |> Repo.all()
  end

  @doc """
  Returns the total count of users in the system.

  ## Examples

      iex> count_users()
      42
  """
  @spec count_users() :: integer()
  def count_users do
    Repo.aggregate(User, :count)
  end

  @doc """
  Deletes a user and creates an audit log entry.

  ## Examples

      iex> delete_user(user_id, superuser_id, "127.0.0.1")
      {:ok, %User{}}

      iex> delete_user(invalid_id, superuser_id, "127.0.0.1")
      ** (Ecto.NoResultsError)
  """
  @spec delete_user(String.t(), String.t(), String.t()) :: {:ok, User.t()} | {:error, any()}
  def delete_user(user_id, superuser_id, ip_address) do
    user = Repo.get!(User, user_id)

    Repo.transaction(fn ->
      # Create audit log before deletion
      create_audit_log(%{
        action: "user_delete",
        superuser_id: superuser_id,
        target_user_id: user_id,
        metadata: %{email: user.email},
        ip_address: ip_address
      })

      # Anonymize login history (GDPR compliance)
      # Preserves IP/location for security analysis, removes user link
      Towerops.Accounts.anonymize_user_login_history(user_id)

      # Anonymize browser sessions (GDPR compliance)
      Towerops.Accounts.anonymize_user_browser_sessions(user_id)

      # Delete user (cascades to memberships and tokens)
      Repo.delete!(user)
    end)
  end

  ## Organization Management

  @doc """
  Lists all organizations in the system with their memberships.

  ## Options

    * `:limit` - Maximum number of organizations to return (default: 100)
    * `:offset` - Number of organizations to skip (default: 0)

  ## Examples

      iex> list_all_organizations()
      [%Organization{}, ...]

      iex> list_all_organizations(limit: 50, offset: 100)
      [%Organization{}, ...]
  """
  def list_all_organizations(opts \\ []) do
    limit = Keyword.get(opts, :limit, 100)
    offset = Keyword.get(opts, :offset, 0)

    Organization
    |> order_by([o], desc: o.inserted_at)
    |> limit(^limit)
    |> offset(^offset)
    |> preload(memberships: :user)
    |> Repo.all()
  end

  @doc """
  Returns the total count of organizations in the system.

  ## Examples

      iex> count_organizations()
      15
  """
  def count_organizations do
    Repo.aggregate(Organization, :count)
  end

  @doc """
  Deletes an organization and creates an audit log entry.

  ## Examples

      iex> delete_organization(org_id, superuser_id, "127.0.0.1")
      {:ok, %Organization{}}

      iex> delete_organization(invalid_id, superuser_id, "127.0.0.1")
      ** (Ecto.NoResultsError)
  """
  def delete_organization(org_id, superuser_id, ip_address) do
    org = Repo.get!(Organization, org_id)

    Repo.transaction(fn ->
      # Create audit log before deletion
      create_audit_log(%{
        action: "org_delete",
        superuser_id: superuser_id,
        target_user_id: nil,
        metadata: %{name: org.name, slug: org.slug},
        ip_address: ip_address
      })

      # Delete organization (cascades to memberships, sites, device)
      Repo.delete!(org)
    end)
  end

  ## Audit Logging

  @doc """
  Creates an audit log entry.

  ## Examples

      iex> create_audit_log(%{action: "impersonate_start", superuser_id: superuser_id, target_user_id: user_id})
      {:ok, %AuditLog{}}

      iex> create_audit_log(%{action: "invalid"})
      {:error, %Ecto.Changeset{}}
  """
  def create_audit_log(attrs) do
    %AuditLog{}
    |> AuditLog.changeset(attrs)
    |> Repo.insert()
  end

  @doc """
  Lists recent audit logs with associated users.

  ## Options

    * `:limit` - Maximum number of logs to return (default: 100)

  ## Examples

      iex> list_audit_logs()
      [%AuditLog{}, ...]

      iex> list_audit_logs(limit: 50)
      [%AuditLog{}, ...]
  """
  def list_audit_logs(opts \\ []) do
    limit = Keyword.get(opts, :limit, 100)

    AuditLog
    |> order_by([a], desc: a.inserted_at)
    |> limit(^limit)
    |> preload([:superuser, :target_user])
    |> Repo.all()
  end

  @doc """
  Returns audit logs related to a specific user (GDPR data access).
  """
  def list_audit_logs_for_user(user_id, opts \\ []) do
    limit = Keyword.get(opts, :limit, 100)

    AuditLog
    |> where([a], a.target_user_id == ^user_id or a.superuser_id == ^user_id)
    |> order_by([a], desc: a.inserted_at)
    |> limit(^limit)
    |> preload([:superuser, :target_user])
    |> Repo.all()
  end
end
