defmodule Towerops.AccountsFixturesTest do
  use Towerops.DataCase

  import Towerops.AccountsFixtures

  alias Towerops.Accounts.UserToken

  describe "unique_user_email/0" do
    test "generates unique email addresses" do
      email1 = unique_user_email()
      email2 = unique_user_email()

      assert email1 != email2
      assert email1 =~ ~r/@example\.com$/
      assert email2 =~ ~r/@example\.com$/
    end
  end

  describe "valid_user_password/0" do
    test "returns valid password" do
      password = valid_user_password()
      assert is_binary(password)
      assert String.length(password) >= 12
    end
  end

  describe "valid_user_attributes/1" do
    test "returns valid user attributes with default email" do
      attrs = valid_user_attributes()
      assert attrs.email =~ ~r/@example\.com$/
    end

    test "merges provided attributes" do
      attrs = valid_user_attributes(%{custom_field: "value"})
      assert attrs.email =~ ~r/@example\.com$/
      assert attrs.custom_field == "value"
    end
  end

  describe "unconfirmed_user_fixture/1" do
    test "creates an unconfirmed user" do
      user = unconfirmed_user_fixture()

      assert user.email =~ ~r/@example\.com$/
      refute user.confirmed_at
    end

    test "creates user with custom attributes" do
      email = "custom@example.com"
      user = unconfirmed_user_fixture(%{email: email})

      assert user.email == email
    end
  end

  describe "user_fixture/1" do
    test "creates a confirmed user" do
      user = user_fixture()

      assert user.email =~ ~r/@example\.com$/
      assert user.confirmed_at
    end

    test "creates user with custom attributes" do
      email = "confirmed@example.com"
      user = user_fixture(%{email: email})

      assert user.email == email
      assert user.confirmed_at
    end
  end

  describe "user_scope_fixture/0 and user_scope_fixture/1" do
    test "creates a user scope without arguments" do
      scope = user_scope_fixture()

      assert scope.user
      assert scope.user.email =~ ~r/@example\.com$/
    end

    test "creates a user scope from existing user" do
      user = user_fixture()
      scope = user_scope_fixture(user)

      assert scope.user.id == user.id
    end
  end

  describe "set_password/1" do
    test "sets password for user" do
      user = user_fixture()
      user_with_password = set_password(user)

      assert user_with_password.id == user.id

      # Verify password was set by attempting login
      assert Towerops.Accounts.get_user_by_email_and_password(
               user_with_password.email,
               valid_user_password()
             )
    end
  end

  describe "generate_user_magic_link_token/1" do
    test "generates a magic link token for user" do
      user = user_fixture()
      {encoded_token, token} = generate_user_magic_link_token(user)

      assert is_binary(encoded_token)
      assert is_binary(token)
      assert encoded_token != token
    end
  end

  describe "offset_user_token/3" do
    test "offsets a user token's timestamps" do
      user = user_fixture()
      {_encoded_token, token} = generate_user_magic_link_token(user)

      assert {1, _} = offset_user_token(token, -1, :day)

      # Verify the token was updated
      user_token = Towerops.Repo.get_by(UserToken, token: token)
      assert user_token
      assert DateTime.before?(user_token.inserted_at, DateTime.utc_now())
    end
  end

  describe "override_token_authenticated_at/2" do
    test "overrides authenticated_at for a token" do
      user = user_fixture()
      {_encoded_token, token} = generate_user_magic_link_token(user)

      custom_time = DateTime.utc_now() |> DateTime.add(-2, :hour) |> DateTime.truncate(:second)
      assert {1, _} = override_token_authenticated_at(token, custom_time)

      # Verify the token was updated
      user_token = Towerops.Repo.get_by(UserToken, token: token)
      assert user_token

      # Compare with truncated time since database might store at second precision
      assert DateTime.diff(user_token.authenticated_at, custom_time, :second) == 0
    end
  end
end
