defmodule Towerops.Workers.ExpiredBanCleanupWorkerTest do
  use Towerops.DataCase, async: false

  alias Towerops.Security.IpBlock
  alias Towerops.Workers.ExpiredBanCleanupWorker

  describe "perform/1" do
    test "deletes expired temporary bans" do
      insert_ip_block(%{
        ip_address: "10.0.0.1",
        banned_until: DateTime.add(DateTime.utc_now(), -60, :second)
      })

      assert :ok = ExpiredBanCleanupWorker.perform(%Oban.Job{args: %{}})

      assert [] = Repo.all(IpBlock)
    end

    test "keeps active temporary bans" do
      block =
        insert_ip_block(%{
          ip_address: "10.0.0.2",
          banned_until: DateTime.add(DateTime.utc_now(), 300, :second)
        })

      assert :ok = ExpiredBanCleanupWorker.perform(%Oban.Job{args: %{}})

      assert Repo.get(IpBlock, block.id)
    end

    test "keeps permanent bans" do
      block =
        insert_ip_block(%{
          ip_address: "10.0.0.3",
          offense_count: 3,
          banned_until: nil
        })

      assert :ok = ExpiredBanCleanupWorker.perform(%Oban.Job{args: %{}})

      assert Repo.get(IpBlock, block.id)
    end

    test "deletes only expired bans among mixed records" do
      expired =
        insert_ip_block(%{
          ip_address: "10.0.0.10",
          banned_until: DateTime.add(DateTime.utc_now(), -120, :second)
        })

      active =
        insert_ip_block(%{
          ip_address: "10.0.0.11",
          banned_until: DateTime.add(DateTime.utc_now(), 600, :second)
        })

      permanent =
        insert_ip_block(%{
          ip_address: "10.0.0.12",
          offense_count: 3,
          banned_until: nil
        })

      assert :ok = ExpiredBanCleanupWorker.perform(%Oban.Job{args: %{}})

      refute Repo.get(IpBlock, expired.id)
      assert Repo.get(IpBlock, active.id)
      assert Repo.get(IpBlock, permanent.id)
    end

    test "returns :ok when no records exist" do
      assert :ok = ExpiredBanCleanupWorker.perform(%Oban.Job{args: %{}})
    end

    test "broadcasts to PubSub when bans are deleted" do
      Phoenix.PubSub.subscribe(Towerops.PubSub, "security:blocks")

      insert_ip_block(%{
        ip_address: "10.0.0.20",
        banned_until: DateTime.add(DateTime.utc_now(), -60, :second)
      })

      assert :ok = ExpiredBanCleanupWorker.perform(%Oban.Job{args: %{}})

      assert_receive :blocks_updated
    end

    test "does not broadcast to PubSub when nothing is deleted" do
      Phoenix.PubSub.subscribe(Towerops.PubSub, "security:blocks")

      assert :ok = ExpiredBanCleanupWorker.perform(%Oban.Job{args: %{}})

      refute_receive :blocks_updated
    end
  end

  defp insert_ip_block(attrs) do
    defaults = %{
      offense_count: 1,
      last_violation_at: DateTime.utc_now(),
      reason: "test ban"
    }

    %IpBlock{}
    |> IpBlock.changeset(Map.merge(defaults, attrs))
    |> Repo.insert!()
  end
end
