# `PhoenixKit.Modules.Shop.Services.ImageMigration`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.71/lib/modules/shop/services/image_migration.ex#L1)

Orchestrates batch migration of product images from external URLs to Storage module.

Provides functions to query migration status, queue migration jobs,
and migrate individual products.

## Usage

    # Get migration statistics
    stats = ImageMigration.migration_stats()
    # => %{total: 100, migrated: 25, pending: 75, failed: 0}

    # Queue all pending products for migration
    {:ok, count} = ImageMigration.queue_all_migrations(user_uuid)
    # => {:ok, 75}

    # Migrate a single product synchronously
    {:ok, product} = ImageMigration.migrate_product(product_uuid, user_uuid)

# `cancel_pending_migrations`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.71/lib/modules/shop/services/image_migration.ex#L238)

```elixir
@spec cancel_pending_migrations() :: {:ok, non_neg_integer()}
```

Cancels all pending migration jobs.

## Returns

  * `{:ok, count}` - Number of jobs cancelled

# `migrate_product`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.71/lib/modules/shop/services/image_migration.ex#L274)

```elixir
@spec migrate_product(String.t(), String.t() | integer()) ::
  {:ok, PhoenixKit.Modules.Shop.Product.t()} | {:error, term()}
```

Migrates a single product synchronously.

Downloads all legacy images and updates the product with storage UUIDs.

## Returns

  * `{:ok, product}` - Updated product with storage image IDs
  * `{:error, :already_migrated}` - Product already has storage images
  * `{:error, :no_images}` - Product has no legacy images to migrate
  * `{:error, reason}` - Migration failed

## Examples

    iex> migrate_product(product_uuid, user_uuid)
    {:ok, %Product{featured_image_uuid: "uuid-1", image_uuids: ["uuid-1", "uuid-2"]}}

# `migration_stats`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.71/lib/modules/shop/services/image_migration.ex#L139)

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

Returns migration statistics.

## Returns

A map with the following keys:
  * `:total` - Total products with any images (legacy or storage)
  * `:migrated` - Products that have storage-based images
  * `:pending` - Products with legacy images but no storage images
  * `:failed` - Count of failed migration jobs (from Oban)
  * `:in_progress` - Count of currently running migration jobs

## Examples

    iex> migration_stats()
    %{total: 100, migrated: 25, pending: 75, failed: 0, in_progress: 5}

# `products_migrated_count`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.71/lib/modules/shop/services/image_migration.ex#L108)

```elixir
@spec products_migrated_count() :: non_neg_integer()
```

Returns the count of products that have been migrated.

## Examples

    iex> products_migrated_count()
    25

# `products_needing_migration`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.71/lib/modules/shop/services/image_migration.ex#L52)

```elixir
@spec products_needing_migration(keyword()) :: [PhoenixKit.Modules.Shop.Product.t()]
```

Returns products that need image migration.

A product needs migration if it has legacy image URLs but no Storage UUIDs.

## Options

  * `:limit` - Maximum number of products to return (default: all)
  * `:offset` - Number of products to skip (default: 0)

## Examples

    iex> products_needing_migration()
    [%Product{}, %Product{}, ...]

    iex> products_needing_migration(limit: 10)
    [%Product{}, ...]

# `products_needing_migration_count`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.71/lib/modules/shop/services/image_migration.ex#L84)

```elixir
@spec products_needing_migration_count() :: non_neg_integer()
```

Returns the count of products needing migration.

## Examples

    iex> products_needing_migration_count()
    75

# `queue_all_migrations`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.71/lib/modules/shop/services/image_migration.ex#L207)

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

Queues migration jobs for all products needing migration.

Creates an Oban job for each product that has legacy images but no storage images.

## Options

  * `:limit` - Maximum number of products to queue (default: all)
  * `:priority` - Oban job priority (default: 3)

## Returns

  * `{:ok, count}` - Number of jobs queued
  * `{:error, reason}` - If queuing failed

## Examples

    iex> queue_all_migrations(user_uuid)
    {:ok, 75}

    iex> queue_all_migrations(user_uuid, limit: 10)
    {:ok, 10}

---

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