defmodule ToweropsWeb.Live.Helpers.AccessControlTest do
  use Towerops.DataCase, async: true

  import Towerops.AccountsFixtures
  import Towerops.DevicesFixtures
  import Towerops.OrganizationsFixtures

  alias Towerops.Alerts
  alias Towerops.Sites
  alias ToweropsWeb.Live.Helpers.AccessControl

  describe "verify_device_access/2" do
    setup do
      user1 = user_fixture()
      user2 = user_fixture()

      org1 = organization_fixture(user1.id)
      org2 = organization_fixture(user2.id)

      site1 = site_fixture(organization_id: org1.id)
      site2 = site_fixture(organization_id: org2.id)

      device1 = device_fixture(%{site: site1, organization: org1})
      device2 = device_fixture(%{site: site2, organization: org2})

      %{
        org1: org1,
        org2: org2,
        site1: site1,
        site2: site2,
        device1: device1,
        device2: device2
      }
    end

    test "returns {:ok, device} when device belongs to organization", %{
      device1: device,
      org1: org
    } do
      assert {:ok, returned_device} = AccessControl.verify_device_access(device.id, org.id)
      assert returned_device.id == device.id
    end

    test "returns {:error, :unauthorized} when device belongs to different organization", %{
      device1: device,
      org2: wrong_org
    } do
      assert {:error, :unauthorized} = AccessControl.verify_device_access(device.id, wrong_org.id)
    end

    test "returns {:error, :not_found} when device does not exist", %{org1: org} do
      fake_id = Ecto.UUID.generate()
      assert {:error, :not_found} = AccessControl.verify_device_access(fake_id, org.id)
    end
  end

  describe "verify_site_access/2" do
    setup do
      user1 = user_fixture()
      user2 = user_fixture()

      org1 = organization_fixture(user1.id)
      org2 = organization_fixture(user2.id)

      site1 = site_fixture(organization_id: org1.id)
      site2 = site_fixture(organization_id: org2.id)

      %{
        org1: org1,
        org2: org2,
        site1: site1,
        site2: site2
      }
    end

    test "returns {:ok, site} when site belongs to organization", %{
      site1: site,
      org1: org
    } do
      assert {:ok, returned_site} = AccessControl.verify_site_access(site.id, org.id)
      assert returned_site.id == site.id
    end

    test "returns {:error, :unauthorized} when site belongs to different organization", %{
      site1: site,
      org2: wrong_org
    } do
      assert {:error, :unauthorized} = AccessControl.verify_site_access(site.id, wrong_org.id)
    end

    test "returns {:error, :not_found} when site does not exist", %{org1: org} do
      fake_id = Ecto.UUID.generate()
      assert {:error, :not_found} = AccessControl.verify_site_access(fake_id, org.id)
    end
  end

  describe "verify_alert_access/2" do
    setup do
      user1 = user_fixture()
      user2 = user_fixture()

      org1 = organization_fixture(user1.id)
      org2 = organization_fixture(user2.id)

      site1 = site_fixture(organization_id: org1.id)
      site2 = site_fixture(organization_id: org2.id)

      device1 = device_fixture(%{site: site1, organization: org1})
      device2 = device_fixture(%{site: site2, organization: org2})

      alert1 = alert_fixture(device_id: device1.id)
      alert2 = alert_fixture(device_id: device2.id)

      %{
        org1: org1,
        org2: org2,
        device1: device1,
        device2: device2,
        alert1: alert1,
        alert2: alert2
      }
    end

    test "returns {:ok, alert} when alert's device belongs to organization", %{
      alert1: alert,
      org1: org
    } do
      assert {:ok, returned_alert} = AccessControl.verify_alert_access(alert.id, org.id)
      assert returned_alert.id == alert.id

      # Verify preloading worked
      assert returned_alert.device
      assert returned_alert.device.site
      assert returned_alert.device.site.organization
    end

    test "returns {:error, :unauthorized} when alert's device belongs to different organization",
         %{
           alert1: alert,
           org2: wrong_org
         } do
      assert {:error, :unauthorized} = AccessControl.verify_alert_access(alert.id, wrong_org.id)
    end

    test "returns {:error, :not_found} when alert does not exist", %{org1: org} do
      fake_id = Ecto.UUID.generate()
      assert {:error, :not_found} = AccessControl.verify_alert_access(fake_id, org.id)
    end
  end

  # Helper functions to create test data
  defp site_fixture(attrs) do
    {:ok, site} =
      attrs
      |> Enum.into(%{
        name: "Test Site #{System.unique_integer([:positive])}"
      })
      |> Sites.create_site()

    site
  end

  defp alert_fixture(attrs) do
    {:ok, alert} =
      attrs
      |> Enum.into(%{
        alert_type: :device_down,
        message: "Test alert",
        triggered_at: DateTime.utc_now()
      })
      |> Alerts.create_alert()

    alert
  end
end
