# `PushX.FCM`
[🔗](https://github.com/cignosystems/pushx/blob/v0.11.0/lib/push_x/fcm.ex#L1)

Firebase Cloud Messaging (FCM) client.

Sends push notifications to Android devices and web browsers using the FCM v1 API
with OAuth2 authentication via Goth.

## Configuration

Add to your config:

    config :pushx,
      fcm_project_id: "my-project-id",
      fcm_credentials: {:file, "priv/keys/firebase-service-account.json"}

## Usage

    # Simple notification
    PushX.FCM.send(device_token, %{
      "notification" => %{
        "title" => "Hello",
        "body" => "World"
      }
    })

    # Using Message struct
    message = PushX.Message.new("Hello", "World")
    PushX.FCM.send(device_token, message)

    # Notification with custom data
    PushX.FCM.send(device_token, %{
      "notification" => %{"title" => "Alert", "body" => "Something happened"},
      "data" => %{"event_id" => "1"}
    })

    # Data-only (silent) message — no visible notification
    PushX.FCM.send_data(device_token, %{action: "sync", id: 123})

    # Data-only via structured payload
    PushX.FCM.send(device_token, %{"data" => %{"action" => "sync"}})

## Web Push (Chrome, Firefox, Edge)

FCM supports web push using the same API. Web tokens come from the browser's
Firebase Messaging SDK (`firebase.messaging().getToken()`).

    # Web push with click action
    PushX.FCM.send(web_token, payload,
      webpush: %{
        "fcm_options" => %{"link" => "https://example.com/page"}
      }
    )

    # Using web notification helper
    payload = PushX.FCM.web_notification("Title", "Body", "https://example.com")
    PushX.FCM.send(web_token, payload)

# `option`

```elixir
@type option() ::
  {:project_id, String.t()}
  | {:data, map()}
  | {:android, map()}
  | {:apns, map()}
  | {:webpush, map()}
```

# `payload`

```elixir
@type payload() :: map() | PushX.Message.t()
```

# `token`

```elixir
@type token() :: String.t()
```

# `notification`

```elixir
@spec notification(String.t(), String.t(), keyword()) :: map()
```

Creates a simple notification payload.

## Examples

    iex> PushX.FCM.notification("Hello", "World")
    %{"title" => "Hello", "body" => "World"}

# `send`

```elixir
@spec send(token(), payload(), [option()]) ::
  {:ok, PushX.Response.t()} | {:error, PushX.Response.t()}
```

Sends a push notification to an Android device with automatic retry.

Uses exponential backoff for transient failures following Google's best practices.
Permanent failures (bad token, invalid argument) are not retried.

## Options

  * `:project_id` - Firebase project ID (default: from config)
  * `:data` - Custom data payload map
  * `:android` - Android-specific configuration
  * `:apns` - APNS configuration (for iOS via FCM)
  * `:webpush` - Web push configuration

## Returns

  * `{:ok, %PushX.Response{}}` on success
  * `{:error, %PushX.Response{}}` on failure

# `send_batch`

```elixir
@spec send_batch([token()], payload(), [option()]) :: [
  {token(), {:ok, PushX.Response.t()} | {:error, PushX.Response.t()}}
]
```

Sends notifications to multiple devices concurrently.

## Options

All standard options plus:
  * `:concurrency` - Max concurrent requests (default: 50)
  * `:timeout` - Timeout per request in ms (default: 30_000)
  * `:validate_tokens` - Validate token format before sending (default: false).
    Invalid tokens get `{:error, %Response{status: :invalid_token}}` without
    hitting the network.

## Returns

A list of `{token, result}` tuples.

# `send_data`

```elixir
@spec send_data(token(), map(), [option()]) ::
  {:ok, PushX.Response.t()} | {:error, PushX.Response.t()}
```

Sends a data-only message (no visible notification) with automatic retry.

# `send_data_once`

```elixir
@spec send_data_once(token(), map(), [option()]) ::
  {:ok, PushX.Response.t()} | {:error, PushX.Response.t()}
```

Sends a data-only message without retry.

# `send_once`

```elixir
@spec send_once(token(), payload(), [option()]) ::
  {:ok, PushX.Response.t()} | {:error, PushX.Response.t()}
```

Sends a push notification without retry.

Use this when you want to handle retries yourself or for testing.

# `send_web`

```elixir
@spec send_web(token(), String.t(), String.t(), String.t(), keyword()) ::
  {:ok, PushX.Response.t()} | {:error, PushX.Response.t()}
```

Sends a web push notification with automatic retry.

Convenience function that combines `web_notification/4` with `send/3`.

## Examples

    PushX.FCM.send_web(web_token, "Hello", "World", "https://example.com")

    # With options
    PushX.FCM.send_web(web_token, "Alert", "Check this out",
      "https://example.com/page",
      icon: "https://example.com/icon.png"
    )

# `web_notification`

```elixir
@spec web_notification(String.t(), String.t(), String.t(), keyword()) :: map()
```

Creates a web push notification payload with click action.

This helper creates a notification optimized for web browsers (Chrome, Firefox, Edge).
The `link` option specifies the URL to open when the notification is clicked.

## Arguments

  * `title` - Notification title
  * `body` - Notification body
  * `link` - URL to open when clicked
  * `opts` - Optional keyword list:
    * `:icon` - Icon URL for the notification
    * `:image` - Large image URL
    * `:badge` - Badge icon URL (small monochrome icon)
    * `:tag` - Tag for notification grouping
    * `:renotify` - Whether to alert again for same tag (default: false)
    * `:require_interaction` - Keep notification until user interacts (default: false)

## Examples

    # Simple web notification
    PushX.FCM.web_notification("New Message", "You have a new message", "https://example.com/messages")

    # With icon and badge
    PushX.FCM.web_notification("Sale!", "50% off today",
      "https://shop.com",
      icon: "https://shop.com/icon.png",
      badge: "https://shop.com/badge.png"
    )

---

*Consult [api-reference.md](api-reference.md) for complete listing*
