defmodule WhoNeedHelpWeb.SecurityHeaders do
  @moduledoc false

  import Plug.Conn

  def put_content_security_policy(conn, _opts) do
    tile_origin =
      :who_need_help
      |> Application.fetch_env!(:map_tile_url)
      |> origin()

    websocket_origin =
      case conn.scheme do
        :http -> origin(%URI{scheme: "ws", host: conn.host, port: conn.port})
        :https -> origin(%URI{scheme: "wss", host: conn.host, port: conn.port})
        _other -> nil
      end

    connect_sources = sources(["'self'", tile_origin, websocket_origin])
    image_sources = sources(["'self'", "data:", "blob:", tile_origin])

    policy =
      [
        "default-src 'self'",
        "base-uri 'self'",
        "frame-ancestors 'none'",
        "object-src 'none'",
        "script-src 'self'",
        "style-src 'self' 'unsafe-inline'",
        "img-src #{image_sources}",
        "connect-src #{connect_sources}",
        "worker-src 'self' blob:",
        "manifest-src 'self'",
        "form-action 'self'"
      ]
      |> Enum.join("; ")

    put_resp_header(conn, "content-security-policy", policy)
  end

  defp sources(values),
    do: values |> Enum.reject(&is_nil/1) |> Enum.uniq() |> Enum.join(" ")

  defp origin(url) when is_binary(url), do: url |> URI.parse() |> origin()

  defp origin(%URI{scheme: scheme, host: host} = uri)
       when scheme in ["http", "https", "ws", "wss"] and is_binary(host) and host != "" do
    URI.to_string(%URI{scheme: scheme, host: host, port: uri.port})
  end

  defp origin(_uri), do: nil
end
