defmodule Towerops.Workers.SessionCleanupWorkerTest do
  use Towerops.DataCase, async: true

  import Towerops.AccountsFixtures

  alias Towerops.Accounts
  alias Towerops.Accounts.BrowserSession
  alias Towerops.Workers.SessionCleanupWorker

  describe "perform/1" do
    test "deletes expired browser sessions" do
      user = user_fixture()
      {_token, user_token} = Accounts.generate_user_session_token_with_record(user)

      # Create an expired session (1 day ago)
      expired_session =
        insert_browser_session(%{
          user_id: user.id,
          user_token_id: user_token.id,
          expires_at: DateTime.add(DateTime.utc_now(), -1, :day)
        })

      # Execute the worker
      assert {:ok, %{sessions_deleted: 1}} =
               SessionCleanupWorker.perform(%Oban.Job{args: %{}})

      # Verify expired session was deleted
      refute Repo.get(BrowserSession, expired_session.id)
    end

    test "keeps non-expired browser sessions" do
      user = user_fixture()
      {_token, user_token} = Accounts.generate_user_session_token_with_record(user)

      # Create a valid session (expires in 1 day)
      valid_session =
        insert_browser_session(%{
          user_id: user.id,
          user_token_id: user_token.id,
          expires_at: DateTime.add(DateTime.utc_now(), 1, :day)
        })

      # Execute the worker
      assert {:ok, %{sessions_deleted: 0}} =
               SessionCleanupWorker.perform(%Oban.Job{args: %{}})

      # Verify valid session still exists
      assert Repo.get(BrowserSession, valid_session.id)
    end

    test "deletes only expired sessions among mixed sessions" do
      user = user_fixture()
      {_token1, user_token1} = Accounts.generate_user_session_token_with_record(user)
      {_token2, user_token2} = Accounts.generate_user_session_token_with_record(user)
      {_token3, user_token3} = Accounts.generate_user_session_token_with_record(user)

      # Create 2 expired sessions
      expired1 =
        insert_browser_session(%{
          user_id: user.id,
          user_token_id: user_token1.id,
          expires_at: DateTime.add(DateTime.utc_now(), -2, :day)
        })

      expired2 =
        insert_browser_session(%{
          user_id: user.id,
          user_token_id: user_token2.id,
          expires_at: DateTime.add(DateTime.utc_now(), -1, :hour)
        })

      # Create 1 valid session
      valid_session =
        insert_browser_session(%{
          user_id: user.id,
          user_token_id: user_token3.id,
          expires_at: DateTime.add(DateTime.utc_now(), 7, :day)
        })

      # Execute the worker
      assert {:ok, %{sessions_deleted: 2}} =
               SessionCleanupWorker.perform(%Oban.Job{args: %{}})

      # Verify expired sessions were deleted
      refute Repo.get(BrowserSession, expired1.id)
      refute Repo.get(BrowserSession, expired2.id)

      # Verify valid session still exists
      assert Repo.get(BrowserSession, valid_session.id)
    end

    test "returns zero count when no expired sessions exist" do
      user = user_fixture()
      {_token, user_token} = Accounts.generate_user_session_token_with_record(user)

      # Create only valid sessions
      insert_browser_session(%{
        user_id: user.id,
        user_token_id: user_token.id,
        expires_at: DateTime.add(DateTime.utc_now(), 1, :day)
      })

      # Execute the worker
      assert {:ok, %{sessions_deleted: 0}} =
               SessionCleanupWorker.perform(%Oban.Job{args: %{}})
    end

    test "works when no sessions exist at all" do
      # Execute the worker with no sessions in the database
      assert {:ok, %{sessions_deleted: 0}} =
               SessionCleanupWorker.perform(%Oban.Job{args: %{}})
    end

    test "handles sessions that expired exactly now" do
      user = user_fixture()
      {_token, user_token} = Accounts.generate_user_session_token_with_record(user)

      now = DateTime.utc_now()

      # Create a session that expires exactly now (should be considered expired)
      expired_now =
        insert_browser_session(%{
          user_id: user.id,
          user_token_id: user_token.id,
          expires_at: now
        })

      # Execute the worker
      assert {:ok, %{sessions_deleted: count}} =
               SessionCleanupWorker.perform(%Oban.Job{args: %{}})

      # The session should be deleted (expires_at < now, not <=)
      # Actually, checking the code: where([bs], bs.expires_at < ^now)
      # So expires_at == now should NOT be deleted
      assert count == 0
      assert Repo.get(BrowserSession, expired_now.id)
    end
  end

  # Helper function to create browser sessions
  defp insert_browser_session(attrs) do
    default_attrs = %{
      ip_address: "192.168.1.1",
      last_activity_at: DateTime.utc_now()
    }

    attrs = Map.merge(default_attrs, attrs)

    %BrowserSession{}
    |> BrowserSession.create_changeset(attrs)
    |> Repo.insert!()
  end
end
