defmodule Towerops.Devices.EventLoggerTest do
  use Towerops.DataCase, async: false

  import Towerops.AccountsFixtures

  alias Towerops.Devices.EventLogger

  describe "event logging via PubSub" do
    setup do
      # Start EventLogger for these tests
      start_supervised!(EventLogger)

      user = user_fixture()

      {:ok, organization} =
        Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)

      {:ok, site} =
        Towerops.Sites.create_site(%{
          name: "Test Site",
          organization_id: organization.id
        })

      {:ok, device} =
        Towerops.Devices.create_device(%{
          name: "Test Equipment",
          site_id: site.id,
          organization_id: site.organization_id,
          ip_address: "192.168.1.1"
        })

      %{user: user, organization: organization, site: site, device: device}
    end

    test "EventLogger logs events broadcast via PubSub", %{device: device} do
      event_attrs = %{
        device_id: device.id,
        event_type: "interface_speed_change",
        severity: "info",
        message: "Interface eth0 speed detected: 1.0 Gbps",
        metadata: %{
          interface_id: "test-interface-id",
          interface_name: "eth0",
          old_speed: nil,
          new_speed: 1_000_000_000
        },
        occurred_at: DateTime.truncate(DateTime.utc_now(), :second)
      }

      # Broadcast event via PubSub
      Phoenix.PubSub.broadcast(
        Towerops.PubSub,
        "device:events",
        {:device_event, event_attrs}
      )

      # Give the EventLogger a moment to process
      Process.sleep(100)

      # Verify event was created in database
      events = Towerops.Devices.list_devices_events(device.id, 10)
      assert length(events) == 1

      event = hd(events)
      assert event.device_id == device.id
      assert event.event_type == "interface_speed_change"
      assert event.severity == "info"
      assert event.message == "Interface eth0 speed detected: 1.0 Gbps"
    end

    test "EventLogger handles multiple events in sequence", %{device: device} do
      now = DateTime.truncate(DateTime.utc_now(), :second)

      # Broadcast multiple events
      for i <- 1..3 do
        Phoenix.PubSub.broadcast(
          Towerops.PubSub,
          "device:events",
          {:device_event,
           %{
             device_id: device.id,
             event_type: "interface_speed_change",
             severity: "info",
             message: "Event #{i}",
             metadata: %{},
             occurred_at: DateTime.add(now, i, :second)
           }}
        )
      end

      # Poll for events with early exit (max 100ms)
      events =
        Enum.reduce_while(1..10, [], fn _, _acc ->
          events = Towerops.Devices.list_devices_events(device.id, 10)

          if length(events) >= 3 do
            {:halt, events}
          else
            Process.sleep(10)
            {:cont, []}
          end
        end)

      # Verify all events were created
      assert length(events) == 3
    end

    test "EventLogger logs errors for invalid events", %{device: device} do
      # Broadcast an invalid event (missing required fields)
      invalid_event = %{
        device_id: device.id
        # Missing required fields
      }

      # Capture log output
      log =
        ExUnit.CaptureLog.capture_log(fn ->
          Phoenix.PubSub.broadcast(
            Towerops.PubSub,
            "device:events",
            {:device_event, invalid_event}
          )

          Process.sleep(100)
        end)

      # Verify error was logged
      assert log =~ "Failed to log event"
    end

    test "EventLogger ignores non-event messages" do
      # Send a message that's not an event
      Phoenix.PubSub.broadcast(
        Towerops.PubSub,
        "device:events",
        {:some_other_message, "data"}
      )

      # Should not crash - just log and move on
      Process.sleep(100)

      # EventLogger should still be running
      assert Process.whereis(EventLogger)
    end
  end
end
