defmodule Towerops.MobileSessions.QRLoginToken do
  @moduledoc """
  Schema for QR code login tokens.

  These are short-lived tokens (5 minutes) that are displayed as QR codes
  in the web interface. Mobile apps scan the QR code and use the token
  to create a new mobile session.
  """
  use Ecto.Schema

  import Ecto.Changeset

  @primary_key {:id, :binary_id, autogenerate: true}
  @foreign_key_type :binary_id

  schema "qr_login_tokens" do
    field :token, :string
    field :expires_at, :utc_datetime
    field :completed_at, :utc_datetime

    belongs_to :user, Towerops.Accounts.User
    belongs_to :mobile_session, Towerops.MobileSessions.MobileSession

    timestamps(type: :utc_datetime)
  end

  @doc """
  Creates a changeset for a new QR login token.

  Token is automatically generated and expires in 5 minutes.
  """
  def create_changeset(qr_login_token, attrs) do
    qr_login_token
    |> cast(attrs, [:user_id, :token, :expires_at])
    |> validate_required([:user_id])
    |> put_token()
    |> put_expiration()
    |> unique_constraint(:token)
  end

  @doc """
  Marks a QR login token as completed.
  """
  def complete_changeset(qr_login_token, mobile_session_id) do
    completed_at = DateTime.truncate(DateTime.utc_now(), :second)
    change(qr_login_token, completed_at: completed_at, mobile_session_id: mobile_session_id)
  end

  defp put_token(changeset) do
    if get_field(changeset, :token) do
      changeset
    else
      put_change(changeset, :token, generate_token())
    end
  end

  defp put_expiration(changeset) do
    if get_field(changeset, :expires_at) do
      changeset
    else
      expires_at = DateTime.utc_now() |> DateTime.add(5, :minute) |> DateTime.truncate(:second)
      put_change(changeset, :expires_at, expires_at)
    end
  end

  # Generate a shorter token for QR codes (32 bytes = 256 bits)
  defp generate_token do
    32 |> :crypto.strong_rand_bytes() |> Base.url_encode64(padding: false)
  end
end
