
# Programación

Queuety incluye compatibilidad integrada con trabajos recurrentes. En lugar de depender de `wp_cron`, las programaciones se guardan en la base de datos y un proceso `wp queuety work` de larga duración (o el proceso principal de un grupo de workers) las evalúa aproximadamente una vez por minuto. `wp queuety work --once`, `wp queuety flush` y el mecanismo alternativo WP-Cron integrado en el plugin no evalúan las programaciones ([#14](https://github.com/inline0/queuety/issues/14)); sin un worker de larga duración, ejecute `wp queuety schedule run` por su cuenta.

## Programación por intervalos

Use `every()` para programaciones sencillas basadas en intervalos. La expresión puede ser cualquier cadena compatible con `DateTimeImmutable::modify()` de PHP:

```php
use Queuety\Queuety;

Queuety::schedule( 'cleanup_handler' )
    ->every( '1 hour' )
    ->on_queue( 'maintenance' );

Queuety::schedule( 'sync_inventory' )
    ->every( '30 minutes' )
    ->on_queue( 'sync' );

Queuety::schedule( 'daily_report', [ 'type' => 'summary' ] )
    ->every( '1 day' );
```

## Programación con Cron

Use `cron()` para expresiones cron estándar de 5 campos:

```php
// Every day at 3am
Queuety::schedule( 'nightly_cleanup' )
    ->cron( '0 3 * * *' );

// Every Monday at 9am
Queuety::schedule( 'weekly_digest' )
    ->cron( '0 9 * * 1' )
    ->on_queue( 'emails' );

// Every 15 minutes
Queuety::schedule( 'health_check' )
    ->cron( '*/15 * * * *' );
```

## Opciones de programación

El generador `PendingSchedule` admite estos métodos:

| Método | Descripción |
|---|---|
| `every( string $interval )` | Establece una expresión de intervalo |
| `cron( string $expression )` | Establece una expresión cron |
| `on_queue( string $queue )` | Establece la cola de destino (predeterminada: `'default'`) |
| `overlap( OverlapPolicy $policy )` | Establece la política de solapamiento (predeterminada: `Allow`) |

Debe llamar a `every()` o a `cron()` antes de que se ejecute la programación. Si no establece ninguno, se produce un `RuntimeException`.

## Políticas de solapamiento

Cuando un trabajo programado sigue en ejecución (pendiente o en proceso) al llegar el siguiente intervalo, la política de solapamiento determina qué ocurre. Establézcala con el método `overlap()`:

```php
use Queuety\Queuety;
use Queuety\Enums\OverlapPolicy;

Queuety::schedule( 'sync_inventory' )
    ->every( '5 minutes' )
    ->overlap( OverlapPolicy::Skip )
    ->on_queue( 'sync' );
```

La enumeración `OverlapPolicy` tiene tres opciones:

| Política | Valor | Comportamiento |
|---|---|---|
| `OverlapPolicy::Allow` | `allow` | Siempre envía un trabajo nuevo, aunque la ejecución anterior siga activa. Es la opción predeterminada. |
| `OverlapPolicy::Skip` | `skip` | Omite el intervalo actual si el handler tiene trabajos pendientes o en proceso. `next_run` se actualiza normalmente para mantener la cadencia de la programación. |
| `OverlapPolicy::Buffer` | `buffer` | Igual que Skip: no se envía el trabajo y `next_run` avanza a la siguiente ocurrencia programada. A diferencia de Skip, `last_run` no se actualiza. |

### Allow (predeterminado)

Pueden ejecutarse varias instancias del mismo handler al mismo tiempo. Use esta opción para handlers idempotentes en los que el solapamiento no cause problemas.

```php
Queuety::schedule( 'send_heartbeat_ping' )
    ->every( '1 minute' )
    ->overlap( OverlapPolicy::Allow );
```

### Skip

El intervalo se omite silenciosamente si el handler sigue en ejecución. Use esta opción para handlers en los que ejecutar una segunda instancia sería innecesario o causaría conflictos.

```php
Queuety::schedule( 'rebuild_search_index' )
    ->every( '10 minutes' )
    ->overlap( OverlapPolicy::Skip );
```

### Buffer

Hoy se comporta como Skip: si el handler todavía tiene un trabajo pendiente o en proceso, no se envía el trabajo y `next_run` avanza a la siguiente ocurrencia programada. La única diferencia es que Buffer deja `last_run` sin cambios, mientras que Skip registra el intervalo omitido como la última ejecución.

```php
Queuety::schedule( 'sync_inventory' )
    ->every( '5 minutes' )
    ->overlap( OverlapPolicy::Buffer );
```

## Cómo funciona

Al crear una programación, Queuety la guarda en la tabla `queuety_schedules` con la expresión, el handler, el payload y la siguiente hora de ejecución calculada.

En cada intervalo del scheduler (aproximadamente una vez por minuto en un worker de larga duración, o cuando ejecuta `wp queuety schedule run`), el scheduler busca programaciones cuya `next_run` ya haya pasado. Para cada programación vencida, envía un trabajo nuevo y actualiza `next_run` a la siguiente ocurrencia.

## Gestión de programaciones mediante CLI

```bash
# List all schedules
wp queuety schedule list

# Add a recurring schedule
wp queuety schedule add cleanup_handler --every="1 hour"
wp queuety schedule add nightly_report --cron="0 3 * * *" --queue=reports

# Remove a schedule
wp queuety schedule remove cleanup_handler

# Manually trigger the scheduler
wp queuety schedule run
```

## Gestión de programaciones mediante PHP

```php
// Get the scheduler instance
$scheduler = Queuety::scheduler();

// List all schedules
$schedules = $scheduler->list();

// Remove a schedule by handler name
$scheduler->remove( 'cleanup_handler' );

// Manually trigger a tick
$count = $scheduler->tick();
```

## Payload

Las programaciones pueden incluir un payload estático que se pasa a cada trabajo enviado:

```php
Queuety::schedule( 'generate_report', [ 'type' => 'daily', 'format' => 'pdf' ] )
    ->every( '1 day' )
    ->on_queue( 'reports' );
```

Cada vez que se activa la programación, se envía un trabajo nuevo con este payload.
