# `PhoenixKitDocumentCreator.GoogleDocsClient`
[🔗](https://github.com/BeamLabEU/phoenix_kit_document_creator/blob/0.2.8/lib/phoenix_kit_document_creator/google_docs_client.ex#L1)

Google Docs and Drive API client for the Document Creator module.

This module provides **direct Google Drive and Docs API access** without
touching the local database. Use it when you need raw Drive operations:
creating files, listing folders, moving files, exporting PDFs, reading
document content, and substituting template variables.

For combined Drive + DB operations, use `PhoenixKitDocumentCreator.Documents`.

## Capabilities

- **Folders**: `find_folder_by_name/2`, `create_folder/2`, `find_or_create_folder/2`,
  `ensure_folder_path/2`, `discover_folders/0`, `list_subfolders/1`
- **Files**: `list_folder_files/1`, `move_file/2`, `copy_file/3`, `create_document/2`
- **Docs**: `get_document/1`, `get_document_text/1`, `batch_update/2`, `replace_all_text/2`
- **Export**: `export_pdf/1`, `fetch_thumbnail/1`
- **Status**: `file_status/1`, `file_location/1`
- **URLs**: `get_edit_url/1`, `get_folder_url/1`

OAuth credentials and tokens are managed by `PhoenixKit.Integrations`
under the `"google"` provider. Folder configuration is stored separately
under the `"document_creator_folders"` settings key.

# `active_provider_key`

```elixir
@spec active_provider_key() :: String.t()
```

Returns the active provider key, including connection slug if configured.

# `batch_update`

```elixir
@spec batch_update(String.t(), [map()]) :: {:ok, map()} | {:error, term()}
```

Send a batchUpdate request to a Google Doc.

# `connection_status`

```elixir
@spec connection_status() :: {:ok, %{email: String.t()}} | {:error, atom()}
```

Check if connected. Returns `{:ok, %{email: email}}` or `{:error, reason}`.

# `copy_file`

```elixir
@spec copy_file(String.t(), String.t(), keyword()) ::
  {:ok, String.t()} | {:error, :invalid_file_id | :copy_failed | term()}
```

Copy a file in Google Drive. Returns the new file's ID.

# `create_document`

```elixir
@spec create_document(
  String.t(),
  keyword()
) ::
  {:ok, %{doc_id: String.t(), name: String.t(), url: String.t() | nil}}
  | {:error, :create_document_failed | term()}
```

Create a new blank Google Doc in a specific folder.

# `create_folder`

```elixir
@spec create_folder(
  String.t(),
  keyword()
) :: {:ok, String.t()} | {:error, :create_folder_failed | term()}
```

Create a folder in Google Drive. Optionally specify a parent folder.
Returns `{:ok, folder_id}`.

# `discover_folders`

```elixir
@spec discover_folders() :: %{
  templates_folder_id: String.t() | nil,
  documents_folder_id: String.t() | nil,
  deleted_templates_folder_id: String.t() | nil,
  deleted_documents_folder_id: String.t() | nil
}
```

Discover templates, documents, and deleted folder IDs.
Looks for folders by name in Drive root, creating them if they don't exist.
Caches results in Settings.

# `ensure_folder_path`

```elixir
@spec ensure_folder_path(
  String.t(),
  keyword()
) :: {:ok, String.t()} | {:error, term()}
```

Walk a path like "clients/active/templates", creating folders as needed.
Returns `{:ok, leaf_folder_id}`.

# `export_pdf`

```elixir
@spec export_pdf(String.t()) ::
  {:ok, binary()} | {:error, :invalid_file_id | :pdf_export_failed | term()}
```

Export a Google Doc as PDF. Returns `{:ok, pdf_binary}`.

# `fetch_thumbnail`

```elixir
@spec fetch_thumbnail(term()) ::
  {:ok, String.t()}
  | {:error,
     :no_doc_id
     | :no_thumbnail
     | :thumbnail_link_failed
     | :thumbnail_fetch_failed
     | :invalid_file_id
     | term()}
```

Fetch a document thumbnail as a base64 data URI via the Drive API.

# `file_location`

```elixir
@spec file_location(term()) ::
  {:ok, %{folder_id: String.t(), path: String.t(), trashed: boolean()}}
  | {:error, :invalid_file_id | :not_found | term()}
```

Resolve the current parent folder and path for a Drive file.

# `file_status`

```elixir
@spec file_status(term()) ::
  {:ok, %{trashed: boolean(), parents: [String.t()]}}
  | {:ok, :not_found}
  | {:error, :invalid_file_id | term()}
```

Fetch Google Drive file metadata needed for sync classification.

# `find_folder_by_name`

```elixir
@spec find_folder_by_name(
  String.t(),
  keyword()
) :: {:ok, String.t()} | {:error, :not_found | :folder_search_failed | term()}
```

Find a folder by name, optionally within a parent folder.
Returns `{:ok, folder_id}` or `{:error, :not_found}`.

# `find_or_create_folder`

```elixir
@spec find_or_create_folder(
  String.t(),
  keyword()
) :: {:ok, String.t()} | {:error, term()}
```

Find a folder by name, or create it if it doesn't exist.
Optionally specify a parent folder.
Returns `{:ok, folder_id}`.

# `folder_settings_key`

```elixir
@spec folder_settings_key() :: String.t()
```

The Settings key used for folder configuration.

# `get_credentials`

```elixir
@spec get_credentials() :: {:ok, map()} | {:error, atom()}
```

Get stored OAuth credentials via PhoenixKit.Integrations.

# `get_document`

```elixir
@spec get_document(String.t()) :: {:ok, map()} | {:error, term()}
```

Read a Google Doc's full content.

# `get_document_text`

```elixir
@spec get_document_text(String.t()) :: {:ok, String.t()} | {:error, term()}
```

Extract plain text content from a Google Doc (for variable detection).

# `get_edit_url`

```elixir
@spec get_edit_url(term()) :: String.t() | nil
```

Get the edit URL for a Google Doc.

# `get_folder_config`

```elixir
@spec get_folder_config() :: map()
```

Get configured folder paths and names from Settings, with defaults.

# `get_folder_ids`

```elixir
@spec get_folder_ids() :: map()
```

Get cached folder IDs from Settings, or discover them.

# `get_folder_url`

```elixir
@spec get_folder_url(term()) :: String.t() | nil
```

Get the Google Drive folder URL.

# `list_folder_files`

```elixir
@spec list_folder_files(String.t()) :: {:ok, [map()]} | {:error, term()}
```

List Google Docs directly in a Drive folder (non-recursive, fully paginated).

Returns `{:ok, [%{"id" => ..., "name" => ..., "modifiedTime" => ..., "thumbnailLink" => ..., "parents" => [...]}]}`.

For recursive traversal across subfolders, use
`PhoenixKitDocumentCreator.GoogleDocsClient.DriveWalker.walk_tree/2`.

# `list_subfolders`

```elixir
@spec list_subfolders(String.t()) :: {:ok, [map()]} | {:error, term()}
```

List subfolders within a parent folder (non-recursive, fully paginated).
Returns `{:ok, [%{"id" => ..., "name" => ...}]}`.

# `move_file`

```elixir
@spec move_file(String.t(), String.t()) ::
  :ok
  | {:error,
     :invalid_file_id | :move_failed | :get_file_parents_failed | term()}
```

Move a file to a different folder in Google Drive.

# `replace_all_text`

```elixir
@spec replace_all_text(String.t(), map()) :: {:ok, map()} | {:error, term()}
```

Replace all `{{variable}}` placeholders in a Google Doc.
Keys are wrapped in `{{ }}` automatically.

# `validate_file_id`

```elixir
@spec validate_file_id(term()) :: {:ok, String.t()} | {:error, :invalid_file_id}
```

Validate a Google Drive file/folder ID. Returns `{:ok, id}` or `{:error, :invalid_file_id}`.

---

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