defmodule Shopifex.Plug.SetCSPHeader do
  @moduledoc """
  Adds Content-Security-Policy response header to the provided `Plug.Conn` in order to securely
  load embedded application in the Shopify admin panel.

  Read more here: https://shopify.dev/apps/store/security/iframe-protection#embedded-apps
  """
  defexception message: "an error occurred when attempting to set CSP headers"

  @shopify_unified_admin_url "https://admin.shopify.com"

  @spec init(options :: Plug.opts()) :: Plug.opts()
  def init(options) do
    # initialize options
    options
  end

  @spec call(conn :: Plug.Conn.t(), opts :: Plug.opts()) :: Plug.Conn.t() | none()
  def call(conn, _) do
    case get_current_shop(conn) do
      {:ok, shop} ->
        url = Shopifex.Shops.get_url(shop)
        allowed_frame_ancestors = [@shopify_unified_admin_url, "https://#{url}"]

        Plug.Conn.put_resp_header(
          conn,
          "content-security-policy",
          "frame-ancestors #{Enum.join(allowed_frame_ancestors, " ")};"
        )

      {:error, :no_current_shop} ->
        raise(__MODULE__,
          message:
            "Cannot set CSP header without shop loaded in session. Ensure that this plug is being called on a `conn` which has been passed through the `Shopifex.Plug.ShopifySession` plug."
        )
    end
  end

  defp get_current_shop(conn) do
    case Shopifex.Plug.current_shop(conn) do
      nil -> {:error, :no_current_shop}
      shop -> {:ok, shop}
    end
  end
end
