defmodule ToweropsWeb.Live.Helpers.AccessControl do
  @moduledoc """
  Centralized organization-based access control for LiveViews.

  Provides functions to verify that users have access to resources
  (devices, sites, alerts) within their organization scope.
  """

  alias Towerops.Alerts
  alias Towerops.Devices
  alias Towerops.Repo
  alias Towerops.Sites

  @doc """
  Verifies that a device belongs to the specified organization.

  Returns `{:ok, device}` if access is granted, or an error tuple.

  ## Examples

      iex> verify_device_access(device_id, org_id)
      {:ok, %Device{}}

      iex> verify_device_access(device_id, wrong_org_id)
      {:error, :unauthorized}

      iex> verify_device_access("nonexistent", org_id)
      {:error, :not_found}
  """
  @spec verify_device_access(binary(), binary()) ::
          {:ok, Devices.Device.t()} | {:error, :not_found | :unauthorized}
  def verify_device_access(device_id, organization_id) do
    case Devices.get_device(device_id) do
      nil ->
        {:error, :not_found}

      device ->
        if device.organization_id == organization_id do
          {:ok, device}
        else
          {:error, :unauthorized}
        end
    end
  end

  @doc """
  Verifies that a site belongs to the specified organization.

  Returns `{:ok, site}` if access is granted, or an error tuple.

  ## Examples

      iex> verify_site_access(site_id, org_id)
      {:ok, %Site{}}

      iex> verify_site_access(site_id, wrong_org_id)
      {:error, :unauthorized}

      iex> verify_site_access("nonexistent", org_id)
      {:error, :not_found}
  """
  @spec verify_site_access(binary(), binary()) ::
          {:ok, Sites.Site.t()} | {:error, :not_found | :unauthorized}
  def verify_site_access(site_id, organization_id) do
    case Sites.get_site(site_id) do
      nil ->
        {:error, :not_found}

      site ->
        if site.organization_id == organization_id do
          {:ok, site}
        else
          {:error, :unauthorized}
        end
    end
  end

  @doc """
  Verifies that an alert's device belongs to the specified organization.

  Returns `{:ok, alert}` if access is granted, or an error tuple.
  The alert is preloaded with `device: [site: :organization]` for access checking.

  ## Examples

      iex> verify_alert_access(alert_id, org_id)
      {:ok, %Alert{device: %Device{site: %Site{organization: %Organization{}}}}}

      iex> verify_alert_access(alert_id, wrong_org_id)
      {:error, :unauthorized}

      iex> verify_alert_access("nonexistent", org_id)
      {:error, :not_found}
  """
  @spec verify_alert_access(binary(), binary()) ::
          {:ok, Alerts.Alert.t()} | {:error, :not_found | :unauthorized}
  def verify_alert_access(alert_id, organization_id) do
    case Alerts.get_alert(alert_id) do
      nil ->
        {:error, :not_found}

      alert ->
        alert = Repo.preload(alert, device: [site: :organization])

        if alert.device.site.organization_id == organization_id do
          {:ok, alert}
        else
          {:error, :unauthorized}
        end
    end
  end
end
