defmodule Zoeyscomputer.ApiKeys do
  @moduledoc """
  The ApiKeys context.
  """
  require Logger

  import Ecto.Query, warn: false
  alias Hex.API.User
  alias Zoeyscomputer.ApiKeys.ApiKey
  alias Zoeyscomputer.Repo
  alias Zoeyscomputer.Users.User

  alias Zoeyscomputer.ApiKeys.ApiKey

  @doc """
  Returns the list of api_keys.

  ## Examples

      iex> list_api_keys()
      [%ApiKey{}, ...]

  """
  def list_api_keys do
    Repo.all(ApiKey)
  end

  @doc """
  Gets a single api_key.

  Raises `Ecto.NoResultsError` if the Api key does not exist.

  ## Examples

      iex> get_api_key!(123)
      %ApiKey{}

      iex> get_api_key!(456)
      ** (Ecto.NoResultsError)

  """
  def get_api_key!(id), do: Repo.get!(ApiKey, id)

  @doc """
  Gets an API key by its token
  """
  def get_api_key_by_token(token) do
    Repo.get_by(ApiKey, token: token)
  end

  @doc """
  Creates an API key for a user.
  """
  def create_api_key(user, attrs \\ %{}) do
    # Convert attrs to string keys and add user_id with string key
    attrs = Map.put(attrs, "user_id", user.id)

    result =
      %ApiKey{}
      |> ApiKey.changeset(attrs)
      |> Repo.insert()

    result
  end

  @doc """
  Updates a api_key.

  ## Examples

      iex> update_api_key(api_key, %{field: new_value})
      {:ok, %ApiKey{}}

      iex> update_api_key(api_key, %{field: bad_value})
      {:error, %Ecto.Changeset{}}

  """
  def update_api_key(%ApiKey{} = api_key, attrs) do
    api_key
    |> ApiKey.changeset(attrs)
    |> Repo.update()
  end

  @doc """
  List all API Keys for a given user
  """
  def list_api_keys(%User{} = user) do
    ApiKey
    |> where([a], a.user_id == ^user.id)
    |> Repo.all()
  end

  @doc """
  Deletes a api_key.

  ## Examples

      iex> delete_api_key(api_key)
      {:ok, %ApiKey{}}

      iex> delete_api_key(api_key)
      {:error, %Ecto.Changeset{}}

  """
  def delete_api_key(%ApiKey{} = api_key) do
    Repo.delete(api_key)
  end

  @doc """
  Returns an `%Ecto.Changeset{}` for tracking api_key changes.

  ## Examples

      iex> change_api_key(api_key)
      %Ecto.Changeset{data: %ApiKey{}}

  """
  def change_api_key(%ApiKey{} = api_key, attrs \\ %{}) do
    ApiKey.changeset(api_key, attrs)
  end

  @doc """
  Validates an API key token and returns associated user.
  Returns nil if token is invalid
  """
  def authenticate_api_key(token) when is_binary(token) do
    case get_api_key_by_token(token) do
      %ApiKey{} = key -> Repo.preload(key, :user).user
      nil -> nil
    end
  end
end
