
# Agendamento

Queuety oferece suporte integrado a jobs recorrentes. Em vez de depender de `wp_cron`, os agendamentos são armazenados no banco de dados e avaliados aproximadamente uma vez por minuto por um processo `wp queuety work` de longa duração (ou pelo processo pai de um pool de workers). `wp queuety work --once`, `wp queuety flush` e o fallback WP-Cron integrado ao plugin não avaliam agendamentos ([#14](https://github.com/inline0/queuety/issues/14)); sem um worker de longa duração, execute `wp queuety schedule run` por conta própria.

## Agendamento por intervalo

Use `every()` para agendamentos simples baseados em intervalos. A expressão é qualquer string compatível com o `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' );
```

## Agendamento Cron

Use `cron()` para expressões padrão de 5 campos cron:

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

## Opções de agendamento

O construtor `PendingSchedule` oferece suporte a estes métodos:

| Método | Descrição |
|---|---|
| `every( string $interval )` | Define uma expressão de intervalo |
| `cron( string $expression )` | Define uma expressão cron |
| `on_queue( string $queue )` | Define a fila de destino (padrão: `'default'`) |
| `overlap( OverlapPolicy $policy )` | Define a política de sobreposição (padrão: `Allow`) |

Você precisa chamar `every()` ou `cron()` antes de o agendamento despachar. Se nenhum dos dois for definido, uma `RuntimeException` será lançada.

## Políticas de sobreposição

Quando um job agendado ainda estiver em execução (pendente ou em processamento) no momento em que o próximo tick ocorrer, a política de sobreposição determinará o que acontece. Defina-a com o método `overlap()`:

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

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

A enumeração `OverlapPolicy` tem três opções:

| Política | Valor | Comportamento |
|---|---|---|
| `OverlapPolicy::Allow` | `allow` | Sempre despacha um novo job, mesmo que a execução anterior ainda esteja ativa. Essa é a opção padrão. |
| `OverlapPolicy::Skip` | `skip` | Ignora o tick atual se o handler tiver jobs pendentes ou em processamento. O `next_run` é atualizado normalmente para manter o ritmo do agendamento. |
| `OverlapPolicy::Buffer` | `buffer` | Assim como em Skip, o tick não é despachado e `next_run` avança para a próxima ocorrência regular. Diferentemente de Skip, `last_run` não é atualizado. |

### Allow (padrão)

Várias instâncias do mesmo handler podem ser executadas ao mesmo tempo. Use essa opção para handlers idempotentes, em que a sobreposição é inofensiva.

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

### Skip

O tick é ignorado silenciosamente se o handler ainda estiver em execução. Use essa opção para handlers em que executar uma segunda instância seria um desperdício ou causaria conflitos.

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

### Buffer

Atualmente, funciona como Skip: quando o handler ainda tem um job pendente ou em processamento, o tick não é despachado e `next_run` avança para a próxima ocorrência regular. A única diferença é que Buffer mantém `last_run` inalterado, enquanto Skip registra o tick ignorado como a última execução.

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

## Como funciona

Quando um agendamento é criado, Queuety o armazena na tabela `queuety_schedules` com a expressão, o handler, o payload e o próximo horário de execução calculado.

A cada tick do agendador (aproximadamente uma vez por minuto em um worker de longa duração, ou quando você executa `wp queuety schedule run`), o agendador verifica os agendamentos cujo `next_run` já passou. Para cada agendamento vencido, despacha um novo job e atualiza `next_run` para a próxima ocorrência.

## Gerenciando agendamentos via 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
```

## Gerenciando agendamentos via 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

Os agendamentos podem incluir um payload estático que é enviado a cada job despachado:

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

Toda vez que o agendamento é acionado, um novo job é despachado com esse payload.
