defmodule Towerops.Accounts.UserCredential do
  @moduledoc """
  Represents a WebAuthn/passkey credential for a user.

  Each credential corresponds to a specific authenticator (device) that
  the user has registered for passwordless authentication.
  """
  use Ecto.Schema

  import Ecto.Changeset

  alias Towerops.Accounts.User

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

  schema "user_credentials" do
    field :credential_id, :binary
    field :public_key, :binary
    field :sign_count, :integer, default: 0
    field :name, :string
    field :last_used_at, :utc_datetime
    field :aaguid, :binary
    field :transports, {:array, :string}, default: []
    field :backup_eligible, :boolean, default: false
    field :backup_state, :boolean, default: false
    field :attestation_format, :string

    belongs_to :user, User

    timestamps(type: :utc_datetime)
  end

  @type t :: %__MODULE__{
          id: Ecto.UUID.t(),
          credential_id: binary(),
          public_key: binary(),
          sign_count: integer(),
          name: String.t(),
          last_used_at: DateTime.t() | nil,
          aaguid: binary(),
          transports: [String.t()],
          backup_eligible: boolean(),
          backup_state: boolean(),
          attestation_format: String.t() | nil,
          user_id: Ecto.UUID.t(),
          user: Ecto.Association.NotLoaded.t() | User.t(),
          inserted_at: DateTime.t(),
          updated_at: DateTime.t()
        }

  @doc """
  Changeset for creating or updating a credential.
  """
  def changeset(credential, attrs) do
    credential
    |> cast(attrs, [
      :user_id,
      :credential_id,
      :public_key,
      :sign_count,
      :name,
      :last_used_at,
      :aaguid,
      :transports,
      :backup_eligible,
      :backup_state,
      :attestation_format
    ])
    |> validate_required([:user_id, :credential_id, :public_key, :name])
    |> validate_length(:name, min: 1, max: 100)
    |> unique_constraint(:credential_id)
    |> foreign_key_constraint(:user_id)
  end

  @doc """
  Changeset for updating credential usage tracking.
  """
  def touch_changeset(credential) do
    change(credential, %{
      last_used_at: DateTime.utc_now(:second),
      sign_count: credential.sign_count + 1
    })
  end
end
