<?php
/**
 * tarefas_cron.php — Lógica das rotinas automáticas, extraída em funções
 * para que possam ser chamadas tanto pelos scripts de cron (cron/*.php)
 * quanto pelo botão "Testar Agora" do painel (api/testar_automacao.php),
 * sem duplicar código.
 */

require_once __DIR__ . '/db_helper.php';
require_once __DIR__ . '/auth.php';
require_once __DIR__ . '/notificacoes.php';

/**
 * Verifica UM único cliente (chamado logo após criar/editar, para não
 * depender do cron rodar 1x por dia — se o cliente já está vencendo hoje
 * ou dentro dos dias configurados, avisa NA HORA, por e-mail direto e
 * WhatsApp enfileirado). Não espera o horário de agendamento configurado,
 * pois é uma ação pontual, não o disparo em massa do cron.
 *
 * Retorna null se nada precisou ser feito, ou um array com o que foi feito.
 */
function notificar_cliente_se_vencendo(string $idCliente): ?array
{
    $hoje = new DateTime('today');
    $config = db_read('config');
    $diasAviso = $config['agendamento_notificacoes']['dias_aviso'] ?? [3];

    $resultado = null;

    db_update('clientes', function (&$clientes) use ($idCliente, $hoje, $diasAviso, $config, &$resultado) {
        foreach ($clientes as &$cliente) {
            if ($cliente['id'] !== $idCliente) {
                continue;
            }
            if (($cliente['status'] ?? 'Ativo') === 'Inativo' || empty($cliente['vencimento'])) {
                return;
            }

            $vencimento = new DateTime($cliente['vencimento']);
            $vencimento->setTime(0, 0, 0);
            $diff = (int) $hoje->diff($vencimento)->format('%r%a');

            if ($diff < 0 || ($diff !== 0 && !in_array($diff, $diasAviso, true))) {
                return; // fora da janela de aviso e não é "vence hoje" — o cron cuida no dia certo
            }

            $diasJaNotificados = $cliente['dias_notificados'] ?? [];
            $chaveDia = $diff; // 0 = hoje, 1/2/3... = dias antes

            $deveAvisar = ($diff === 0 || in_array($diff, $diasAviso, true)) && !in_array($chaveDia, $diasJaNotificados, true);
            if (!$deveAvisar) {
                return;
            }

            $idOperador = $cliente['criado_por'] ?? '';
            $mensagens = resolver_mensagens_para_usuario($idOperador);
            $chaveTemplate = $diff === 0 ? 'aviso_vencimento_dia' : 'aviso_3_dias';
            $texto = montar_mensagem($mensagens[$chaveTemplate] ?? '', $cliente, $diff);

            $canaisRenovacao = resolver_canais_para_usuario($idOperador)['cobranca'] ?? ['whatsapp' => true, 'email' => true, 'sms' => false];
            $enviosImediatos = [];

            if (!empty($cliente['whatsapp'])) {
                enfileirar_whatsapp($cliente['whatsapp'], $texto, $idOperador, $cliente['id'] ?? null);
                $enviosImediatos[] = 'whatsapp (fila)';
            }
            if (!empty($cliente['email'])) {
                // Registra na fila (para aparecer em "Filas de Envio" com
                // histórico) e já tenta enviar NA HORA (não espera o
                // horário/intervalo agendado, pois é uma checagem pontual
                // — diferente do disparo em massa do cron).
                $assuntoEmail = ($diff === 0 ? 'Vence HOJE - ' : "Vence em {$diff} dia(s) - ") . ($cliente['nome'] ?? '');
                $corpoEmail = nl2br(htmlspecialchars($texto));
                $idItemFila = enfileirar_email($cliente['email'], $assuntoEmail, $corpoEmail, $cliente['id'] ?? null, $idOperador);

                $ok = enviar_email($cliente['email'], $assuntoEmail, $corpoEmail, $idOperador);

                db_update('fila_emails', function (&$fila) use ($idItemFila, $ok) {
                    foreach ($fila as &$item) {
                        if ($item['id'] === $idItemFila) {
                            $item['tentativas'] = 1;
                            $item['status'] = $ok ? 'enviado' : 'erro';
                            if ($ok) {
                                $item['enviado_em'] = date('Y-m-d H:i:s');
                            }
                            break;
                        }
                    }
                });
                $enviosImediatos[] = 'email (' . ($ok ? 'enviado' : 'falhou') . ')';
            }

            $diasJaNotificados[] = $chaveDia;
            $cliente['dias_notificados'] = $diasJaNotificados;
            $cliente['notificado_3_dias'] = in_array(3, $diasJaNotificados, true);
            $cliente['notificado_no_dia'] = in_array(0, $diasJaNotificados, true);

            $resultado = ['dias' => $diff, 'canais' => $enviosImediatos];
        }
    });

    if ($resultado) {
        auditoria_log('SISTEMA', 'AVISO_INSTANTANEO_CADASTRO', "Cliente {$idCliente}: " . json_encode($resultado, JSON_UNESCAPED_UNICODE));
    }

    return $resultado;
}

/**
 * Varre os clientes e ENFILEIRA (não envia direto) os avisos de vencimento
 * conforme os dias configurados em agendamento_notificacoes.dias_aviso,
 * mais o aviso do dia do vencimento. Também inativa quem está 2+ dias
 * atrasado. Retorna um resumo do que foi feito.
 */
function rodar_verificacao_vencimentos(): array
{
    $hoje = new DateTime('today');

    $config = db_read('config');
    $diasAviso = $config['agendamento_notificacoes']['dias_aviso'] ?? [3];
    $diasAvisoPosVencimento = $config['agendamento_notificacoes']['dias_aviso_pos_vencimento'] ?? [1];

    $processados = ['avisos_enfileirados' => 0, 'no_dia_enfileirados' => 0, 'pos_vencimento_enfileirados' => 0, 'expirados_enfileirados' => 0, 'inativados' => 0, 'clientes_avisados' => []];

    db_update('clientes', function (&$clientes) use ($hoje, $diasAviso, $diasAvisoPosVencimento, &$processados) {
        foreach ($clientes as &$cliente) {

            if (($cliente['status'] ?? 'Ativo') === 'Inativo') {
                continue;
            }
            if (($cliente['tipo'] ?? 'cliente') === 'teste') {
                continue; // testes grátis usam rodar_verificacao_testes() (lógica por minutos, não por dia)
            }
            if (empty($cliente['vencimento'])) {
                continue;
            }
            // Cliente pode optar por não receber lembretes automáticos
            // (cadastro/edição tem uma caixinha "Cobrança automática").
            if (($cliente['cobranca_automatica'] ?? true) === false) {
                continue;
            }

            $vencimento = new DateTime($cliente['vencimento']);
            $vencimento->setTime(0, 0, 0);
            $diff = (int) $hoje->diff($vencimento)->format('%r%a');

            $idOperador = $cliente['criado_por'] ?? '';
            $diasJaNotificados = $cliente['dias_notificados'] ?? [];

            // ---- Cliente já passou do prazo mas ainda não foi inativado
            // (hoje, isso só acontece em diff === -1, já que a inativação
            // automática ocorre em diff <= -2) — dispara o aviso de "vencido"
            // uma única vez, se o dia de atraso estiver marcado em
            // dias_aviso_pos_vencimento (config em Mensagens > Agendamento).
            // Blindagem extra: mesmo já filtrado lá em cima, garante de novo
            // aqui que NUNCA dispara "depois de vencido"/"expirado" pra
            // cliente do tipo teste (só clientes definitivos).
            $ehTeste = ($cliente['tipo'] ?? 'cliente') === 'teste';

            if (!$ehTeste && $diff < 0 && $diff > -2 && in_array(-$diff, $diasAvisoPosVencimento, true) && !in_array($diff, $diasJaNotificados, true)) {
                $mensagens = resolver_mensagens_para_usuario($idOperador);
                $canaisCobranca = resolver_canais_para_usuario($idOperador)['cobranca'] ?? ['whatsapp' => true, 'email' => true, 'sms' => false];
                $diasAtraso = abs($diff);
                $texto = montar_mensagem($mensagens['aviso_pos_vencimento'] ?? '', $cliente, $diasAtraso);

                if (!empty($canaisCobranca['whatsapp']) && !empty($cliente['whatsapp'])) {
                    enfileirar_whatsapp($cliente['whatsapp'], $texto, $idOperador, $cliente['id'] ?? null);
                }
                if (!empty($canaisCobranca['email']) && !empty($cliente['email'])) {
                    $htmlSeguro = nl2br(htmlspecialchars($texto));
                    enfileirar_email($cliente['email'], "Vencido há {$diasAtraso} dia(s) - " . ($cliente['nome'] ?? ''), $htmlSeguro, $cliente['id'] ?? null, $idOperador);
                }
                if (!empty($canaisCobranca['sms']) && !empty($cliente['whatsapp'])) {
                    enfileirar_sms($cliente['whatsapp'], $texto, $cliente['id'] ?? null, $idOperador);
                }

                $diasJaNotificados[] = $diff;
                $cliente['dias_notificados'] = $diasJaNotificados;
                $processados['pos_vencimento_enfileirados']++;
                $processados['clientes_avisados'][] = ($cliente['nome'] ?? '') . " ({$diasAtraso}d atraso)";
            }

            if ($diff <= -2) {
                // Dispara o aviso de "expirado" (uma única vez, no momento
                // exato da inativação — depois disso o cliente vira
                // Inativo e é pulado no topo do loop nas próximas execuções,
                // então não tem risco de reenviar). Blindagem extra: nunca
                // dispara pra cliente do tipo teste.
                if (!$ehTeste) {
                    $mensagens = resolver_mensagens_para_usuario($idOperador);
                    $canaisCobranca = resolver_canais_para_usuario($idOperador)['cobranca'] ?? ['whatsapp' => true, 'email' => true, 'sms' => false];
                    $template = $mensagens['aviso_expirado'] ?? '';
                    if ($template) {
                        $texto = montar_mensagem($template, $cliente, abs($diff));
                        if (!empty($canaisCobranca['whatsapp']) && !empty($cliente['whatsapp'])) {
                            enfileirar_whatsapp($cliente['whatsapp'], $texto, $idOperador, $cliente['id'] ?? null);
                        }
                        if (!empty($canaisCobranca['email']) && !empty($cliente['email'])) {
                            $htmlSeguro = nl2br(htmlspecialchars($texto));
                            enfileirar_email($cliente['email'], 'Acesso suspenso - ' . ($cliente['nome'] ?? ''), $htmlSeguro, $cliente['id'] ?? null, $idOperador);
                        }
                        if (!empty($canaisCobranca['sms']) && !empty($cliente['whatsapp'])) {
                            enfileirar_sms($cliente['whatsapp'], $texto, $cliente['id'] ?? null, $idOperador);
                        }
                        $processados['expirados_enfileirados']++;
                    }
                }

                $cliente['status'] = 'Inativo';
                $processados['inativados']++;
                auditoria_log('CRON', 'CLIENTE_INATIVADO', "Cliente {$cliente['id']} inativado por atraso.");
                continue;
            }

            if (!empty($cliente['notificado_3_dias']) && !in_array(3, $diasJaNotificados, true)) {
                $diasJaNotificados[] = 3;
            }
            if (!empty($cliente['notificado_no_dia']) && !in_array(0, $diasJaNotificados, true)) {
                $diasJaNotificados[] = 0;
            }

            // Resolve os templates por HIERARQUIA DO OPERADOR (cada
            // Revenda/Sub-Revenda pode ter seus próprios textos; se não
            // configurar, usa o de quem a criou, ou o padrão do sistema).
            // O PIX é resolvido internamente pelo montar_mensagem().
            $idOperador = $cliente['criado_por'] ?? '';
            $mensagens = resolver_mensagens_para_usuario($idOperador);
            $canaisCobranca = resolver_canais_para_usuario($idOperador)['cobranca'] ?? ['whatsapp' => true, 'email' => true, 'sms' => false];

            if ($diff > 0 && in_array($diff, $diasAviso, true) && !in_array($diff, $diasJaNotificados, true)) {
                $texto = montar_mensagem($mensagens['aviso_3_dias'] ?? '', $cliente, $diff);

                if (!empty($canaisCobranca['whatsapp']) && !empty($cliente['whatsapp'])) {
                    enfileirar_whatsapp($cliente['whatsapp'], $texto, $idOperador, $cliente['id'] ?? null);
                }
                if (!empty($canaisCobranca['email']) && !empty($cliente['email'])) {
                    $htmlSeguro = nl2br(htmlspecialchars($texto));
                    enfileirar_email($cliente['email'], "Vence em {$diff} dia(s) - " . ($cliente['nome'] ?? ''), $htmlSeguro, $cliente['id'] ?? null, $idOperador);
                }
                if (!empty($canaisCobranca['sms']) && !empty($cliente['whatsapp'])) {
                    enfileirar_sms($cliente['whatsapp'], $texto, $cliente['id'] ?? null, $idOperador);
                }

                $diasJaNotificados[] = $diff;
                $processados['avisos_enfileirados']++;
                $processados['clientes_avisados'][] = ($cliente['nome'] ?? '') . " ({$diff}d)";
            }

            if ($diff === 0 && !in_array(0, $diasJaNotificados, true)) {
                $texto = montar_mensagem($mensagens['aviso_vencimento_dia'] ?? '', $cliente, 0);

                if (!empty($canaisCobranca['whatsapp']) && !empty($cliente['whatsapp'])) {
                    enfileirar_whatsapp($cliente['whatsapp'], $texto, $idOperador, $cliente['id'] ?? null);
                }
                if (!empty($canaisCobranca['email']) && !empty($cliente['email'])) {
                    $htmlSeguro = nl2br(htmlspecialchars($texto));
                    enfileirar_email($cliente['email'], 'Vence HOJE - ' . ($cliente['nome'] ?? ''), $htmlSeguro, $cliente['id'] ?? null, $idOperador);
                }
                if (!empty($canaisCobranca['sms']) && !empty($cliente['whatsapp'])) {
                    enfileirar_sms($cliente['whatsapp'], $texto, $cliente['id'] ?? null, $idOperador);
                }

                $diasJaNotificados[] = 0;
                $processados['no_dia_enfileirados']++;
                $processados['clientes_avisados'][] = ($cliente['nome'] ?? '') . ' (hoje)';
            }

            $cliente['dias_notificados'] = $diasJaNotificados;
            $cliente['notificado_3_dias'] = in_array(3, $diasJaNotificados, true);
            $cliente['notificado_no_dia'] = in_array(0, $diasJaNotificados, true);
        }
    }, []);

    auditoria_log('CRON', 'EXECUCAO_DIARIA', json_encode($processados, JSON_UNESCAPED_UNICODE) . ' [tarefas_cron v2026-08-06-teste-blindado]');
    return $processados;
}

/**
 * Verifica clientes do tipo "teste" (teste grátis, geralmente com poucas
 * HORAS de duração — não dias) e dispara DOIS avisos independentes, cada
 * um só uma vez por teste:
 *  1) Aviso PRÉVIO — X minutos ANTES de expirar (configurável em Mensagens
 *     > Agendamento > "Avisar X minutos antes de expirar"), usando o
 *     template "teste_aviso_previo".
 *  2) Aviso de EXPIRAÇÃO — depois que o tempo já acabou de verdade
 *     (respeitando um prazo máximo de atraso, pra não avisar tardio
 *     demais), usando o template "teste_expirando".
 * Os canais usados são os mesmos pros dois (Mensagens > Canais > Teste
 * Expirado). Diferente de rodar_verificacao_vencimentos() (que só compara
 * o DIA), aqui a comparação é por MINUTOS, porque um teste de 6h não cabe
 * na lógica de "3/2/1 dia antes".
 */
function rodar_verificacao_testes(): array
{
    $agora = new DateTime('now');
    $config = db_read('config');
    $minutosAntes = max(1, (int) ($config['agendamento_notificacoes']['minutos_antes_aviso_teste'] ?? 15));
    $minutosAposMax = max(1, (int) ($config['agendamento_notificacoes']['minutos_aviso_teste'] ?? 60));

    $processados = ['avisos_previos' => 0, 'avisos_expirado' => 0, 'clientes_avisados' => []];

    db_update('clientes', function (&$clientes) use ($agora, $minutosAntes, $minutosAposMax, &$processados) {
        foreach ($clientes as &$cliente) {
            if (($cliente['tipo'] ?? 'cliente') !== 'teste') {
                continue;
            }
            if (($cliente['status'] ?? 'Ativo') === 'Inativo' || empty($cliente['vencimento'])) {
                continue;
            }

            $vencimento = new DateTime($cliente['vencimento']);
            $minutosRestantes = ($vencimento->getTimestamp() - $agora->getTimestamp()) / 60;
            $idOperador = $cliente['criado_por'] ?? '';
            $canaisTeste = resolver_canais_para_usuario($idOperador)['teste'] ?? ['whatsapp' => true, 'email' => true, 'sms' => true];

            // ---- 1) Aviso PRÉVIO (X minutos antes de expirar) ----
            $jaAvisadoPrevio = !empty($cliente['teste_aviso_previo_enviado']);
            if (!$jaAvisadoPrevio && $minutosRestantes > 0 && $minutosRestantes <= $minutosAntes) {
                $mensagens = resolver_mensagens_para_usuario($idOperador);
                $template = $mensagens['teste_aviso_previo'] ?? '';
                if ($template) {
                    $texto = montar_mensagem($template, $cliente);

                    if (!empty($canaisTeste['whatsapp']) && !empty($cliente['whatsapp'])) {
                        enfileirar_whatsapp($cliente['whatsapp'], $texto, $idOperador, $cliente['id'] ?? null);
                    }
                    if (!empty($canaisTeste['email']) && !empty($cliente['email'])) {
                        $htmlSeguro = nl2br(htmlspecialchars($texto));
                        enfileirar_email($cliente['email'], 'Seu teste grátis está acabando - ' . ($cliente['nome'] ?? ''), $htmlSeguro, $cliente['id'] ?? null, $idOperador);
                    }
                    if (!empty($canaisTeste['sms']) && !empty($cliente['whatsapp'])) {
                        enfileirar_sms($cliente['whatsapp'], $texto, $cliente['id'] ?? null, $idOperador);
                    }

                    $cliente['teste_aviso_previo_enviado'] = true;
                    $processados['avisos_previos']++;
                    $processados['clientes_avisados'][] = ($cliente['nome'] ?? '') . ' (aviso prévio)';
                }
            }

            // ---- 2) Aviso de EXPIRAÇÃO (depois que já acabou) ----
            $jaAvisadoExpirado = !empty($cliente['teste_notificado']);
            $minutosDesdeExpirou = -$minutosRestantes;
            if (!$jaAvisadoExpirado && $minutosDesdeExpirou >= 0 && $minutosDesdeExpirou <= $minutosAposMax) {
                $mensagens = resolver_mensagens_para_usuario($idOperador);
                $template = $mensagens['teste_expirando'] ?? '';
                if ($template) {
                    $texto = montar_mensagem($template, $cliente);

                    if (!empty($canaisTeste['whatsapp']) && !empty($cliente['whatsapp'])) {
                        enfileirar_whatsapp($cliente['whatsapp'], $texto, $idOperador, $cliente['id'] ?? null);
                    }
                    if (!empty($canaisTeste['email']) && !empty($cliente['email'])) {
                        $htmlSeguro = nl2br(htmlspecialchars($texto));
                        enfileirar_email($cliente['email'], 'Seu teste grátis acabou - ' . ($cliente['nome'] ?? ''), $htmlSeguro, $cliente['id'] ?? null, $idOperador);
                    }
                    if (!empty($canaisTeste['sms']) && !empty($cliente['whatsapp'])) {
                        enfileirar_sms($cliente['whatsapp'], $texto, $cliente['id'] ?? null, $idOperador);
                    }

                    $cliente['teste_notificado'] = true;
                    $processados['avisos_expirado']++;
                    $processados['clientes_avisados'][] = ($cliente['nome'] ?? '') . ' (expirado)';
                }
            }
        }
    }, []);

    if ($processados['avisos_previos'] > 0 || $processados['avisos_expirado'] > 0) {
        auditoria_log('CRON', 'TESTES_AVISADOS', json_encode($processados, JSON_UNESCAPED_UNICODE));
    }
    return $processados;
}

/**

 * Processa a fila de e-mails (fila_emails.json). Por padrão respeita o
 * horário de início configurado; passe $ignorarHorario=true para forçar o
 * envio imediato mesmo fora do horário (usado pelo botão "Testar Agora").
 */
function rodar_fila_emails(bool $ignorarHorario = false): array
{
    $arquivoTrava = sys_get_temp_dir() . '/cobranex_fila_emails.lock';
    $travaFp = fopen($arquivoTrava, 'c');
    if (!$travaFp || !flock($travaFp, LOCK_EX | LOCK_NB)) {
        return ['ok' => false, 'motivo' => 'Já existe um processamento de fila de e-mails em andamento. Tente novamente em instantes.'];
    }

    $resultado = ['ok' => true, 'enviados' => 0, 'falhas' => 0, 'aguardando_horario' => false, 'detalhes' => []];

    try {
        $config = db_read('config');
        $agenda = $config['agendamento_notificacoes'] ?? [];
        $horarioInicio = $agenda['horario_inicio'] ?? '09:00';
        $intervaloSegundos = max(1, (int) ($agenda['intervalo_segundos_email'] ?? 30));
        $lotePorExecucao = max(1, (int) ($agenda['lote_por_execucao_email'] ?? 10));

        if (!$ignorarHorario) {
            $agora = new DateTime('now');
            [$horaConfig, $minutoConfig] = array_map('intval', explode(':', $horarioInicio) + [0, 0]);
            $limiteInicio = (clone $agora)->setTime($horaConfig, $minutoConfig, 0);
            if ($agora < $limiteInicio) {
                $resultado['aguardando_horario'] = true;
                return $resultado;
            }
        }

        $lote = db_update('fila_emails', function (&$fila) use ($lotePorExecucao) {
            $selecionadas = [];
            foreach ($fila as &$item) {
                if ($item['status'] === 'pendente') {
                    $item['status'] = 'processando';
                    $selecionadas[] = &$item;
                    if (count($selecionadas) >= $lotePorExecucao) {
                        break;
                    }
                }
            }
            return $selecionadas;
        });

        if (empty($lote)) {
            return $resultado;
        }

        foreach ($lote as $i => $item) {
            $sucesso = enviar_email($item['destinatario'], $item['assunto'], $item['corpo_html'], $item['id_operador'] ?? null);

            db_update('fila_emails', function (&$fila) use ($item, $sucesso) {
                foreach ($fila as &$f) {
                    if ($f['id'] === $item['id']) {
                        $f['tentativas'] = ($f['tentativas'] ?? 0) + 1;
                        if ($sucesso) {
                            $f['status'] = 'enviado';
                            $f['enviado_em'] = date('Y-m-d H:i:s');
                        } else {
                            $f['status'] = $f['tentativas'] >= 3 ? 'erro' : 'pendente';
                        }
                        break;
                    }
                }
            });

            if ($sucesso) {
                $resultado['enviados']++;
                auditoria_log('CRON_EMAIL', 'EMAIL_ENVIADO', $item['destinatario']);
            } else {
                $resultado['falhas']++;
                auditoria_log('CRON_EMAIL', 'EMAIL_FALHOU', $item['destinatario']);
            }
            $resultado['detalhes'][] = ($sucesso ? '✅ ' : '❌ ') . $item['destinatario'];

            if ($i < count($lote) - 1) {
                sleep($intervaloSegundos);
            }
        }
    } finally {
        flock($travaFp, LOCK_UN);
        fclose($travaFp);
    }

    return $resultado;
}

/**
 * Processa a fila de SMS (fila_sms.json). Mesmo padrão do e-mail: respeita
 * o horário de início configurado, a menos que $ignorarHorario seja true.
 */
function rodar_fila_sms(bool $ignorarHorario = false): array
{
    $arquivoTrava = sys_get_temp_dir() . '/cobranex_fila_sms.lock';
    $travaFp = fopen($arquivoTrava, 'c');
    if (!$travaFp || !flock($travaFp, LOCK_EX | LOCK_NB)) {
        return ['ok' => false, 'motivo' => 'Já existe um processamento de fila de SMS em andamento. Tente novamente em instantes.'];
    }

    $resultado = ['ok' => true, 'enviados' => 0, 'falhas' => 0, 'aguardando_horario' => false, 'detalhes' => []];

    try {
        $config = db_read('config');
        $agenda = $config['agendamento_notificacoes'] ?? [];
        $horarioInicio = $agenda['horario_inicio'] ?? '09:00';
        $intervaloSegundos = max(1, (int) ($agenda['intervalo_segundos_sms'] ?? 30));
        $lotePorExecucao = max(1, (int) ($agenda['lote_por_execucao_sms'] ?? 10));

        if (!$ignorarHorario) {
            $agora = new DateTime('now');
            [$horaConfig, $minutoConfig] = array_map('intval', explode(':', $horarioInicio) + [0, 0]);
            $limiteInicio = (clone $agora)->setTime($horaConfig, $minutoConfig, 0);
            if ($agora < $limiteInicio) {
                $resultado['aguardando_horario'] = true;
                return $resultado;
            }
        }

        $lote = db_update('fila_sms', function (&$fila) use ($lotePorExecucao) {
            $selecionadas = [];
            foreach ($fila as &$item) {
                if ($item['status'] === 'pendente') {
                    $item['status'] = 'processando';
                    $selecionadas[] = &$item;
                    if (count($selecionadas) >= $lotePorExecucao) {
                        break;
                    }
                }
            }
            return $selecionadas;
        });

        if (empty($lote)) {
            return $resultado;
        }

        foreach ($lote as $i => $item) {
            $sucesso = enviar_sms($item['numero'], $item['mensagem'], $item['id_operador'] ?? '');

            db_update('fila_sms', function (&$fila) use ($item, $sucesso) {
                foreach ($fila as &$f) {
                    if ($f['id'] === $item['id']) {
                        $f['tentativas'] = ($f['tentativas'] ?? 0) + 1;
                        if ($sucesso) {
                            $f['status'] = 'enviado';
                            $f['enviado_em'] = date('Y-m-d H:i:s');
                        } else {
                            $f['status'] = $f['tentativas'] >= 3 ? 'erro' : 'pendente';
                        }
                        break;
                    }
                }
            });

            if ($sucesso) {
                $resultado['enviados']++;
            } else {
                $resultado['falhas']++;
            }
            $resultado['detalhes'][] = ($sucesso ? '✅ ' : '❌ ') . $item['numero'];

            if ($i < count($lote) - 1) {
                sleep($intervaloSegundos);
            }
        }
    } finally {
        flock($travaFp, LOCK_UN);
        fclose($travaFp);
    }

    return $resultado;
}
