
# Pianificazione

Queuety supporta nativamente i processi ricorrenti. Invece di affidarsi a `wp_cron`, le pianificazioni vengono archiviate nel database e valutate circa una volta al minuto da un processo `wp queuety work` a esecuzione prolungata (o dal processo principale di un pool di worker). `wp queuety work --once`, `wp queuety flush` e il fallback WP-Cron integrato nel plugin non valutano le pianificazioni ([#14](https://github.com/inline0/queuety/issues/14)); senza un worker a esecuzione prolungata, esegui tu `wp queuety schedule run`.

## Pianificazione a intervalli

Usa `every()` per pianificazioni semplici basate su intervalli. L'espressione è qualsiasi stringa compatibile con PHP `DateTimeImmutable::modify()`:

```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' );
```

## Pianificazione Cron

Usa `cron()` per le espressioni cron standard a 5 campi:

```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 * * * *' );
```

## Opzioni di pianificazione

Il builder `PendingSchedule` supporta questi metodi:

| Metodo | Descrizione |
|---|---|
| `every( string $interval )` | Imposta un'espressione di intervallo |
| `cron( string $expression )` | Imposta un'espressione cron |
| `on_queue( string $queue )` | Imposta la coda di destinazione (predefinita: `'default'`) |
| `overlap( OverlapPolicy $policy )` | Imposta la politica di sovrapposizione (predefinita: `Allow`) |

Prima dell'invio della pianificazione, devi chiamare `every()` oppure `cron()`. Se non ne imposti nessuno, viene generato un `RuntimeException`.

## Politiche di sovrapposizione

Quando un processo pianificato è ancora in esecuzione (in attesa o in elaborazione) al momento del tick successivo, la politica di sovrapposizione determina cosa accade. Impostala con il metodo `overlap()`:

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

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

L'enum `OverlapPolicy` ha tre casi:

| Politica | Valore | Comportamento |
|---|---|---|
| `OverlapPolicy::Allow` | `allow` | Invia sempre un nuovo processo, anche se l'esecuzione precedente è ancora attiva. È l'impostazione predefinita. |
| `OverlapPolicy::Skip` | `skip` | Salta il tick corrente se l'handler ha processi in attesa o in elaborazione. `next_run` viene aggiornato normalmente affinché la pianificazione mantenga la cadenza. |
| `OverlapPolicy::Buffer` | `buffer` | Come per Skip, il tick non viene inviato e `next_run` avanza alla successiva occorrenza regolare. A differenza di Skip, `last_run` non viene aggiornato. |

### Allow (predefinita)

Più istanze dello stesso handler possono essere eseguite contemporaneamente. Usa questa opzione per handler idempotenti, per i quali la sovrapposizione non crea problemi.

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

### Skip

Il tick viene ignorato senza avvisi se l'handler è ancora in esecuzione. Usalo per handler per cui una seconda istanza sarebbe inutile o causerebbe conflitti.

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

### Buffer

Oggi si comporta come Skip: se l'handler ha ancora un processo in attesa o in elaborazione, il tick non viene inviato e `next_run` passa alla successiva occorrenza regolare. L'unica differenza è che Buffer lascia `last_run` invariato, mentre Skip registra il tick ignorato come ultima esecuzione.

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

## Come funziona

Quando viene creata una pianificazione, Queuety la salva nella tabella `queuety_schedules` con l'espressione, l'handler, il payload e l'orario calcolato della prossima esecuzione.

A ogni tick dello scheduler (circa una volta al minuto in un worker a esecuzione prolungata, oppure quando esegui `wp queuety schedule run`), lo scheduler controlla le pianificazioni il cui `next_run` è trascorso. Per ciascuna pianificazione in scadenza, invia un nuovo processo e aggiorna `next_run` alla successiva occorrenza.

## Gestione delle pianificazioni tramite 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
```

## Gestione delle pianificazioni tramite 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

Le pianificazioni possono includere un payload statico, passato a ogni processo inviato:

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

Ogni volta che scatta la pianificazione, viene inviato un nuovo processo con questo payload.
