<?php
/**
 * api/clientes.php — CRUD + ações rápidas (renovar, histórico) de clientes.
 * Contrato: JSON de entrada/saída. Ação definida por ?acao= ou campo 'acao' no POST.
 */

require_once __DIR__ . '/../core/db_helper.php';
require_once __DIR__ . '/../core/auth.php';
require_once __DIR__ . '/../core/notificacoes.php';
require_once __DIR__ . '/../core/tarefas_cron.php';
require_once __DIR__ . '/../core/IntegracaoService.php';

header('Content-Type: application/json; charset=utf-8');
$usuarioLogado = exigir_login();

$acao = $_GET['acao'] ?? $_POST['acao'] ?? 'listar';
$corpo = json_decode(file_get_contents('php://input'), true) ?? $_POST;

function responder($dados, int $codigo = 200): void
{
    http_response_code($codigo);
    echo json_encode($dados, JSON_UNESCAPED_UNICODE);
    exit;
}

$escopo = escopo_visibilidade($usuarioLogado); // null = ADM vê tudo

try {
    switch ($acao) {

    case 'listar':
        $clientes = db_read('clientes');
        if ($escopo !== null) {
            $clientes = array_values(array_filter($clientes, fn($c) => in_array($c['criado_por'] ?? '', $escopo, true)));
        }
        // A lista normal de Clientes NUNCA mostra testes grátis — eles têm
        // tela própria ("Testes Grátis"), separada do cadastro pago normal.
        if (($_GET['filtro'] ?? '') !== 'testes_gratis') {
            $clientes = array_values(array_filter($clientes, fn($c) => ($c['tipo'] ?? 'cliente') !== 'teste'));
        }
        // Filtro opcional por status (ex: inativos com +2 dias)
        if (($_GET['filtro'] ?? '') === 'inativos') {
            $clientes = array_values(array_filter($clientes, fn($c) => ($c['status'] ?? '') === 'Inativo'));
        }
        // Testes Grátis: mostra TODOS os testes (em andamento + já
        // expirados), pra acompanhar tudo numa tela só, separada do
        // cadastro normal. Ordena por vencimento (quem vence primeiro, topo).
        if (($_GET['filtro'] ?? '') === 'testes_gratis') {
            $clientes = array_values(array_filter($clientes, fn($c) => ($c['tipo'] ?? '') === 'teste'));
            usort($clientes, fn($a, $b) => strcmp($a['vencimento'] ?? '', $b['vencimento'] ?? ''));
        }
        responder(['ok' => true, 'clientes' => $clientes]);
        break;

    case 'criar_teste':
        // Cadastro manual de Teste Grátis, feito pelo próprio painel (fora
        // do fluxo automático do formulário externo em api/teste_gratis.php).
        // Fica separado do cadastro normal (tipo='teste') até alguém
        // converter em cliente de verdade.
        $nome = trim($corpo['nome'] ?? '');
        $whatsapp = preg_replace('/\D/', '', $corpo['whatsapp'] ?? '');
        $email = trim($corpo['email'] ?? '');
        $servidor = trim($corpo['servidor'] ?? '');
        $horas = (float) ($corpo['horas'] ?? 6);
        if ($horas <= 0) {
            $horas = 6;
        }

        if (!$nome || !$whatsapp) {
            responder(['ok' => false, 'erro' => 'Nome e WhatsApp são obrigatórios.'], 422);
        }

        $vencimento = (new DateTime('now'))->modify('+' . (int) round($horas * 60) . ' minutes');

        $novoTeste = [
            'id' => gerar_id('CLI-'),
            'nome' => $nome,
            'whatsapp' => $whatsapp,
            'email' => $email,
            'servidor' => $servidor,
            'plano' => "Teste Grátis {$horas}h",
            'tipo' => 'teste',
            'valor' => 0,
            'usuario_painel' => '',
            'senha_painel' => '',
            'vencimento' => $vencimento->format('Y-m-d H:i:s'),
            'status' => 'Ativo',
            'teste_notificado' => false,
            'criado_por' => $usuarioLogado['id'],
            'criado_em' => date('Y-m-d H:i:s'),
            'logs' => [['data' => date('Y-m-d H:i:s'), 'acao' => "Teste grátis de {$horas}h criado manualmente por " . $usuarioLogado['usuario']]],
        ];

        db_update('clientes', function (&$clientes) use ($novoTeste) {
            $clientes[] = $novoTeste;
        });

        auditoria_log($usuarioLogado['usuario'], 'TESTE_GRATIS_CRIADO_MANUAL', "{$novoTeste['id']} ({$nome}) - {$horas}h");
        responder(['ok' => true, 'cliente' => $novoTeste]);
        break;

    case 'converter_em_cliente':
        // Tira o cliente da tela "Testes Grátis" e transforma em cadastro
        // normal — some da lista de testes e passa a aparecer em Clientes.
        // O operador deve editar depois pra ajustar plano/valor/vencimento
        // reais (aqui só muda o "tipo" e reseta o histórico de notificação).
        $id = $corpo['id'] ?? '';
        $atualizado = db_update('clientes', function (&$clientes) use ($id, $usuarioLogado) {
            foreach ($clientes as &$c) {
                if ($c['id'] === $id) {
                    $c['tipo'] = 'cliente';
                    $c['teste_notificado'] = false;
                    $c['notificado_3_dias'] = false;
                    $c['notificado_no_dia'] = false;
                    $c['dias_notificados'] = [];
                    $c['cobranca_automatica'] = true;
                    $c['logs'][] = ['data' => date('Y-m-d H:i:s'), 'acao' => 'Convertido de Teste Grátis para cliente normal por ' . $usuarioLogado['usuario']];
                    return $c;
                }
            }
            return null;
        });

        if (!$atualizado) {
            responder(['ok' => false, 'erro' => 'Teste não encontrado.'], 404);
        }
        auditoria_log($usuarioLogado['usuario'], 'TESTE_CONVERTIDO_EM_CLIENTE', $id);
        responder(['ok' => true, 'cliente' => $atualizado]);
        break;


    case 'criar':
        $nome = trim($corpo['nome'] ?? '');
        $whatsapp = preg_replace('/\D/', '', $corpo['whatsapp'] ?? '');
        if (!$nome || !$whatsapp) {
            responder(['ok' => false, 'erro' => 'Nome e WhatsApp são obrigatórios.'], 422);
        }

        $nomeServidor = $corpo['servidor'] ?? '';
        if ($nomeServidor) {
            $servidores = db_read('servidores');
            $servidorEscolhido = null;
            foreach ($servidores as $s) {
                if ($s['nome'] === $nomeServidor) { $servidorEscolhido = $s; break; }
            }
            if ($servidorEscolhido) {
                $maximo = (int) ($servidorEscolhido['estoque_maximo'] ?? 0);
                if ($maximo > 0) {
                    $clientesAtuais = db_read('clientes');
                    $ocupados = 0;
                    foreach ($clientesAtuais as $c) {
                        if (($c['status'] ?? '') === 'Ativo' && ($c['tipo'] ?? 'cliente') !== 'teste' && ($c['servidor'] ?? '') === $nomeServidor) {
                            $ocupados++;
                        }
                    }
                    if ($ocupados >= $maximo) {
                        responder(['ok' => false, 'erro' => "O servidor \"{$nomeServidor}\" está sem vagas disponíveis ({$ocupados}/{$maximo}). Libere uma vaga ou aumente o estoque máximo."], 409);
                    }
                }
            }
        }

        $novo = [
            'id' => gerar_id('CLI-'),
            'nome' => $nome,
            'whatsapp' => $whatsapp,
            'email' => trim($corpo['email'] ?? ''),
            'servidor' => $nomeServidor,
            'plano' => $corpo['plano'] ?? '',
            'plano_meses' => (int) ($corpo['plano_meses'] ?? 1),
            'valor' => (float) ($corpo['valor'] ?? 0),
            'desconto_valor' => (float) ($corpo['desconto_valor'] ?? 0),
            'desconto_ativo' => filter_var($corpo['desconto_ativo'] ?? false, FILTER_VALIDATE_BOOLEAN),
            'usuario_painel' => $corpo['usuario_painel'] ?? '',
            'senha_painel' => $corpo['senha_painel'] ?? '',
            // Integração externa: username do cliente no provedor parceiro
            // (pode ser diferente do usuario_painel local) e nº de conexões
            // simultâneas enviado no payload de renovação da API externa.
            'usuario_externo' => trim($corpo['usuario_externo'] ?? ''),
            'conexoes' => max(1, (int) ($corpo['conexoes'] ?? 1)),
            'vencimento' => normalizar_vencimento($corpo['vencimento'] ?? null),
            'status' => 'Ativo',
            'tipo' => 'cliente',
            // Caixinhas do formulário: por padrão ambas ligadas, a menos que
            // o operador desmarque explicitamente ao cadastrar.
            'cobranca_automatica' => !array_key_exists('cobranca_automatica', $corpo) || filter_var($corpo['cobranca_automatica'], FILTER_VALIDATE_BOOLEAN),
            'notificado_3_dias' => false,
            'notificado_no_dia' => false,
            'dias_notificados' => [],
            'criado_por' => $usuarioLogado['id'],
            'criado_em' => date('Y-m-d H:i:s'),
            'logs' => [['data' => date('Y-m-d H:i:s'), 'acao' => 'Cliente cadastrado por ' . $usuarioLogado['usuario']]],
        ];

        db_update('clientes', function (&$clientes) use ($novo) {
            $clientes[] = $novo;
        });

        // Mensagem de boas-vindas é opcional (caixinha do formulário) — por
        // padrão vai marcada, mas o operador pode desmarcar.
        $enviarBoasVindas = !array_key_exists('enviar_boas_vindas', $corpo) || filter_var($corpo['enviar_boas_vindas'], FILTER_VALIDATE_BOOLEAN);
        if ($enviarBoasVindas) {
            disparar_notificacao_multicanal($novo, 'boas_vindas');
        }

        // Caixinha "Pago?" no cadastro: só lança no Financeiro se marcada
        // como Sim (é opt-in, não acontece nada por padrão). Usa o valor
        // JÁ COM o desconto aplicado (se o desconto estiver ativo).
        $foiPago = filter_var($corpo['pago'] ?? false, FILTER_VALIDATE_BOOLEAN);
        if ($foiPago) {
            $valorPago = valor_efetivo_cliente($novo);
            db_update('financeiro', function (&$financeiro) use ($novo, $usuarioLogado, $valorPago) {
                $financeiro[] = [
                    'id' => gerar_id('FIN-'),
                    'tipo' => 'entrada',
                    'origem' => 'cadastro_pago',
                    'cliente_id' => $novo['id'],
                    'criado_por' => $usuarioLogado['id'],
                    'valor' => $valorPago,
                    'descricao' => 'Cadastro (pago no ato) - ' . ($novo['nome'] ?? ''),
                    'data' => date('Y-m-d H:i:s'),
                ];
            });
        }

        // Nenhum aviso de vencimento é disparado na hora do cadastro — isso
        // fica só por conta do cron automático (fila) ou dos botões manuais
        // (Cobrar / Mensagem), como definido no painel.
        auditoria_log($usuarioLogado['usuario'], 'CLIENTE_CRIADO', $novo['id']);
        responder(['ok' => true, 'cliente' => $novo]);
        break;

    case 'editar':
        $id = $corpo['id'] ?? '';
        $atualizado = db_update('clientes', function (&$clientes) use ($id, $corpo, $usuarioLogado) {
            foreach ($clientes as &$c) {
                if ($c['id'] === $id) {
                    $vencimentoAntigo = $c['vencimento'] ?? '';
                    foreach (['nome', 'whatsapp', 'email', 'servidor', 'plano', 'plano_meses', 'valor', 'desconto_valor', 'desconto_ativo', 'usuario_painel', 'senha_painel', 'usuario_externo', 'conexoes', 'vencimento', 'status'] as $campo) {
                        if (array_key_exists($campo, $corpo)) {
                            if ($campo === 'vencimento') {
                                $c[$campo] = normalizar_vencimento($corpo[$campo], $c['vencimento'] ?? '');
                            } elseif ($campo === 'desconto_valor') {
                                $c[$campo] = (float) $corpo[$campo];
                            } elseif ($campo === 'desconto_ativo') {
                                $c[$campo] = filter_var($corpo[$campo], FILTER_VALIDATE_BOOLEAN);
                            } elseif ($campo === 'conexoes') {
                                $c[$campo] = max(1, (int) $corpo[$campo]);
                            } elseif ($campo === 'usuario_externo') {
                                $c[$campo] = trim((string) $corpo[$campo]);
                            } else {
                                $c[$campo] = $corpo[$campo];
                            }
                        }
                    }
                    if (array_key_exists('cobranca_automatica', $corpo)) {
                        $c['cobranca_automatica'] = filter_var($corpo['cobranca_automatica'], FILTER_VALIDATE_BOOLEAN);
                    }
                    // Se a edição mudou o DIA do vencimento (não só reformatou
                    // o mesmo dia/hora), reseta o histórico de notificações —
                    // senão o cron/"Gerar Fila Agora" acha que o cliente "já
                    // foi avisado hoje" (do dia antigo) e pula ele em silêncio,
                    // mesmo a nova data já estando dentro da janela de aviso.
                    if (array_key_exists('vencimento', $corpo)) {
                        $diaAntigo = $vencimentoAntigo ? substr($vencimentoAntigo, 0, 10) : null;
                        $diaNovo = substr($c['vencimento'], 0, 10);
                        if ($diaAntigo !== $diaNovo) {
                            $c['notificado_3_dias'] = false;
                            $c['notificado_no_dia'] = false;
                            $c['dias_notificados'] = [];
                        }
                    }
                    $c['logs'][] = ['data' => date('Y-m-d H:i:s'), 'acao' => 'Cadastro editado por ' . $usuarioLogado['usuario']];
                    return $c;
                }

            }
            return null;
        });

        if (!$atualizado) {
            responder(['ok' => false, 'erro' => 'Cliente não encontrado.'], 404);
        }
        // Editar NÃO dispara nenhuma mensagem instantânea sobre a nova data
        // — isso é feito só pela automação (cron/fila) ou manualmente pelos
        // botões Cobrar/Mensagem, nunca automaticamente ao salvar a edição.
        auditoria_log($usuarioLogado['usuario'], 'CLIENTE_EDITADO', $id);
        responder(['ok' => true, 'cliente' => $atualizado]);
        break;

    case 'deletar':
        $id = $corpo['id'] ?? '';
        db_update('clientes', function (&$clientes) use ($id) {
            $clientes = array_values(array_filter($clientes, fn($c) => $c['id'] !== $id));
        });
        auditoria_log($usuarioLogado['usuario'], 'CLIENTE_DELETADO', $id);
        responder(['ok' => true]);
        break;

    case 'renovar':
        $id = $corpo['id'] ?? '';
        // Por padrão envia a mensagem de confirmação de renovação; o operador
        // pode desmarcar essa opção no painel.
        $enviarMensagem = !array_key_exists('enviar_mensagem', $corpo) || filter_var($corpo['enviar_mensagem'], FILTER_VALIDATE_BOOLEAN);

        $clienteRenovado = db_update('clientes', function (&$clientes) use ($id, $usuarioLogado) {
            foreach ($clientes as &$c) {
                if ($c['id'] === $id) {
                    $meses = max(1, (int) ($c['plano_meses'] ?? 1));
                    $base = new DateTime($c['vencimento'] ?? 'today');
                    $hora = (int) $base->format('H');
                    $minuto = (int) $base->format('i');
                    if ($base < new DateTime('today')) {
                        $base = new DateTime('today');
                        $base->setTime($hora, $minuto, 0); // mantém o horário original mesmo se estava atrasado
                    }
                    $base->modify("+{$meses} months");
                    $c['vencimento'] = $base->format('Y-m-d H:i:s');
                    $c['notificado_3_dias'] = false;
                    $c['notificado_no_dia'] = false;
                    $c['dias_notificados'] = [];
                    $c['status'] = 'Ativo';
                    $c['logs'][] = ['data' => date('Y-m-d H:i:s'), 'acao' => 'Renovado manualmente por ' . $usuarioLogado['usuario']];
                    return $c;
                }
            }
            return null;
        });

        if (!$clienteRenovado) {
            responder(['ok' => false, 'erro' => 'Cliente não encontrado.'], 404);
        }

        $meses = max(1, (int) ($clienteRenovado['plano_meses'] ?? 1));
        $valorRecebido = valor_efetivo_cliente($clienteRenovado);

        // Busca o custo mensal do servidor vinculado a este cliente para
        // calcular o lucro líquido da renovação (entrada - custo do período).
        $custoServidor = 0.0;
        if (!empty($clienteRenovado['servidor'])) {
            $servidores = db_read('servidores');
            foreach ($servidores as $s) {
                if ($s['nome'] === $clienteRenovado['servidor']) {
                    $custoServidor = (float) ($s['custo_mensal'] ?? 0);
                    break;
                }
            }
        }
        $custoTotalPeriodo = round($custoServidor * $meses, 2);
        $lucroLiquido = round($valorRecebido - $custoTotalPeriodo, 2);

        db_update('financeiro', function (&$financeiro) use ($clienteRenovado, $usuarioLogado, $valorRecebido, $custoTotalPeriodo) {
            $financeiro[] = [
                'id' => gerar_id('FIN-'),
                'tipo' => 'entrada',
                'origem' => 'renovacao_manual',
                'cliente_id' => $clienteRenovado['id'],
                'criado_por' => $usuarioLogado['id'],
                'valor' => $valorRecebido,
                'descricao' => 'Renovação - ' . ($clienteRenovado['nome'] ?? '') . ' por ' . $usuarioLogado['usuario'],
                'data' => date('Y-m-d H:i:s'),
            ];
            if ($custoTotalPeriodo > 0) {
                $financeiro[] = [
                    'id' => gerar_id('FIN-'),
                    'tipo' => 'saida',
                    'origem' => 'custo_servidor',
                    'cliente_id' => $clienteRenovado['id'],
                    'criado_por' => $usuarioLogado['id'],
                    'valor' => $custoTotalPeriodo,
                    'descricao' => 'Custo do servidor (' . ($clienteRenovado['servidor'] ?? '') . ') - ' . ($clienteRenovado['nome'] ?? ''),
                    'data' => date('Y-m-d H:i:s'),
                ];
            }
        });

        // Sincroniza a renovação com o provedor externo (se o servidor do
        // cliente tiver integração configurada). Nunca interrompe o fluxo
        // de renovação do Cobranex em caso de falha — só fica registrado
        // em log/auditoria para o operador conferir.
        $syncIntegracao = integracao_sincronizar_renovacao_cliente($clienteRenovado, 'manual');

        $mensagemWhatsApp = null;
        $enviosAutomaticosRenovacao = [];
        if ($enviarMensagem) {
            $canaisRenovacao = resolver_canais_para_usuario($clienteRenovado['criado_por'] ?? null)['renovacao'] ?? ['whatsapp' => true, 'email' => true, 'sms' => false];
            $mensagens = resolver_mensagens_para_usuario($clienteRenovado['criado_por'] ?? null);
            $textoEscolhidoRenovacao = escolher_variacao_cliente($clienteRenovado, $mensagens['confirmacao_renovacao'] ?? [], 'confirmacao_renovacao');
            $textoConfirmacao = montar_mensagem($textoEscolhidoRenovacao, $clienteRenovado);

            // E-mail e SMS (se habilitados) vão pra fila — processados em
            // segundo plano pelo cron, sem travar a resposta desta ação.
            if (!empty($canaisRenovacao['email']) && !empty($clienteRenovado['email'])) {
                enfileirar_email($clienteRenovado['email'], 'Renovação confirmada - ' . ($clienteRenovado['nome'] ?? ''), nl2br(htmlspecialchars($textoConfirmacao)), $clienteRenovado['id'] ?? null, $clienteRenovado['criado_por'] ?? null);
                $enviosAutomaticosRenovacao[] = 'email';
            }
            if (!empty($canaisRenovacao['sms']) && !empty($clienteRenovado['whatsapp'])) {
                enfileirar_sms($clienteRenovado['whatsapp'], $textoConfirmacao, $clienteRenovado['id'] ?? null, $usuarioLogado['id']);
                $enviosAutomaticosRenovacao[] = 'sms';
            }
            // WhatsApp (se habilitado) volta pronto para o operador enviar
            // com um clique — o painel não controla nenhum bot de fato.
            if (!empty($canaisRenovacao['whatsapp']) && !empty($clienteRenovado['whatsapp'])) {
                $mensagemWhatsApp = $textoConfirmacao;
            }

            // Confirmar renovação é sempre um envio de verdade (mesmo que
            // só o WhatsApp manual tenha voltado pronto) — avança o
            // rodízio da confirmação e das tags de saudação/despedida.
            avancar_rotacao_cliente($clienteRenovado['id'], ['confirmacao_renovacao', 'saudacao', 'despedida']);
        }

        auditoria_log($usuarioLogado['usuario'], 'CLIENTE_RENOVADO', $id . " | recebido=R$ {$valorRecebido} | custo=R$ {$custoTotalPeriodo} | lucro=R$ {$lucroLiquido}");
        responder([
            'ok' => true,
            'cliente' => $clienteRenovado,
            'valor_recebido' => $valorRecebido,
            'custo_servidor' => $custoTotalPeriodo,
            'lucro_liquido' => $lucroLiquido,
            'whatsapp' => $clienteRenovado['whatsapp'] ?? '',
            'mensagem_whatsapp' => $mensagemWhatsApp,
            'integracao_externa' => $syncIntegracao,
        ]);
        break;

    case 'historico':
        $id = $_GET['id'] ?? '';
        $clientes = db_read('clientes');
        foreach ($clientes as $c) {
            if ($c['id'] === $id) {
                responder(['ok' => true, 'logs' => $c['logs'] ?? []]);
            }
        }
        responder(['ok' => false, 'erro' => 'Cliente não encontrado.'], 404);
        break;

    case 'mensagem_avulsa':
        // Recebe o texto BRUTO do template já escolhido no painel (fixo,
        // extra, ou digitado na hora), substitui as tags e devolve o texto
        // pronto. Só ENVIA de verdade por e-mail/SMS quando a ação é
        // "Abrir WhatsApp" (confirma o envio) — o botão "Copiar" manda
        // apenas_texto=true e nunca dispara nada em nenhum canal, é
        // puramente ler o texto para colar manualmente.
        $id = $corpo['id'] ?? '';
        $textoBruto = (string) ($corpo['texto'] ?? '');
        $apenasTexto = filter_var($corpo['apenas_texto'] ?? false, FILTER_VALIDATE_BOOLEAN);
        if (!$textoBruto) {
            responder(['ok' => false, 'erro' => 'Informe o texto da mensagem.'], 422);
        }

        $clientes = db_read('clientes');
        $clienteAlvo = null;
        foreach ($clientes as $c) {
            if ($c['id'] === $id) { $clienteAlvo = $c; break; }
        }
        if (!$clienteAlvo) {
            responder(['ok' => false, 'erro' => 'Cliente não encontrado.'], 404);
        }

        $hoje = new DateTime('today');
        $venc = new DateTime($clienteAlvo['vencimento'] ?? 'today');
        $venc->setTime(0, 0, 0);
        $diasRestantes = (int) $hoje->diff($venc)->format('%r%a');

        $texto = montar_mensagem($textoBruto, $clienteAlvo, max(0, $diasRestantes));

        // Mensagem avulsa é uma ação manual explícita do operador: envia
        // e-mail automaticamente (se o cliente tiver e-mail cadastrado) e
        // respeita o canal de SMS configurado para Cobrança — mas só
        // quando NÃO for o botão "Copiar" (apenas_texto).
        $enviosAutomaticos = [];
        if (!$apenasTexto) {
            $canaisCobranca = resolver_canais_para_usuario($clienteAlvo['criado_por'] ?? null)['cobranca'] ?? ['whatsapp' => true, 'email' => true, 'sms' => false];

            if (!empty($clienteAlvo['email'])) {
                enfileirar_email($clienteAlvo['email'], 'Mensagem - ' . ($clienteAlvo['nome'] ?? ''), nl2br(htmlspecialchars($texto)), $clienteAlvo['id'] ?? null, $clienteAlvo['criado_por'] ?? null);
                $enviosAutomaticos[] = 'email';
            }
            if (!empty($canaisCobranca['sms']) && !empty($clienteAlvo['whatsapp'])) {
                enfileirar_sms($clienteAlvo['whatsapp'], $texto, $clienteAlvo['id'] ?? null, $usuarioLogado['id']);
                $enviosAutomaticos[] = 'sms';
            }
            // Mensagem avulsa é texto livre (não é um dos 5 tipos com lista
            // de variações), mas as tags {SAUDACAO}/{DESPEDIDA} sempre
            // rodam — avança elas aqui, só quando é envio de verdade.
            avancar_rotacao_cliente($clienteAlvo['id'], ['saudacao', 'despedida']);
        }

        responder([
            'ok' => true,
            'whatsapp' => $clienteAlvo['whatsapp'] ?? '',
            'texto' => $texto,
            'envios_automaticos' => $enviosAutomaticos,
        ]);
        break;

    case 'gerar_mensagem_copia':
        // Endpoint só-leitura (nunca envia nada em nenhum canal) usado pelo
        // botão "Copiar" com as opções de tipo: cobrança (igual ao botão
        // "Cobrar"), renovação (confirmação — útil quando o pagamento foi
        // confirmado fora do sistema, sem clicar em "Renovar") e "depois de
        // expirado" (aviso de acesso suspenso).
        $id = $_GET['id'] ?? $corpo['id'] ?? '';
        $tipo = $_GET['tipo'] ?? $corpo['tipo'] ?? 'cobranca';
        $clientes = db_read('clientes');
        $clienteAlvo = null;
        foreach ($clientes as $c) {
            if ($c['id'] === $id) {
                $clienteAlvo = $c;
                break;
            }
        }
        if (!$clienteAlvo) {
            responder(['ok' => false, 'erro' => 'Cliente não encontrado.'], 404);
        }

        $hoje = new DateTime('today');
        $venc = new DateTime($clienteAlvo['vencimento'] ?? 'today');
        $venc->setTime(0, 0, 0);
        $diasRestantes = (int) $hoje->diff($venc)->format('%r%a');

        $mensagens = resolver_mensagens_para_usuario($clienteAlvo['criado_por'] ?? null);

        switch ($tipo) {
            case 'renovacao':
                $chaveTemplate = 'confirmacao_renovacao';
                $textoEscolhido = escolher_variacao_cliente($clienteAlvo, $mensagens[$chaveTemplate] ?? [], $chaveTemplate);
                $texto = montar_mensagem($textoEscolhido, $clienteAlvo, max(0, $diasRestantes));
                break;
            case 'expirado':
                $chaveTemplate = 'aviso_expirado';
                $textoEscolhido = escolher_variacao_cliente($clienteAlvo, $mensagens[$chaveTemplate] ?? [], $chaveTemplate);
                $texto = montar_mensagem($textoEscolhido, $clienteAlvo, max(0, -$diasRestantes));
                break;
            case 'cobranca':
            default:
                $chaveTemplate = $diasRestantes <= 0 ? 'aviso_vencimento_dia' : 'aviso_3_dias';
                $textoEscolhido = escolher_variacao_cliente($clienteAlvo, $mensagens[$chaveTemplate] ?? [], $chaveTemplate);
                $texto = montar_mensagem($textoEscolhido, $clienteAlvo, max(0, $diasRestantes));
                break;
        }

        if (!trim($texto)) {
            responder(['ok' => false, 'erro' => 'Esse template está vazio. Configure o texto em Mensagens.'], 422);
        }

        responder(['ok' => true, 'texto' => $texto, 'tipo' => $tipo]);
        break;

    case 'gerar_cobranca':
        // Monta a mensagem de cobrança (com PIX). Por padrão (usado pelo
        // botão "Cobrar"), o WhatsApp volta pronto pro operador enviar com
        // um clique, e e-mail/SMS (se habilitados) são disparados na hora.
        // Quando "enviar=0" (usado pelo botão "Copiar"), a função só monta
        // e devolve o texto — não envia nada em nenhum canal.
        $deveEnviar = ($_GET['enviar'] ?? $corpo['enviar'] ?? '1') !== '0';
        $id = $_GET['id'] ?? $corpo['id'] ?? '';
        $clientes = db_read('clientes');
        $clienteAlvo = null;
        foreach ($clientes as $c) {
            if ($c['id'] === $id) {
                $clienteAlvo = $c;
                break;
            }
        }
        if (!$clienteAlvo) {
            responder(['ok' => false, 'erro' => 'Cliente não encontrado.'], 404);
        }

        $canais = resolver_canais_para_usuario($clienteAlvo['criado_por'] ?? null)['cobranca'] ?? ['whatsapp' => true, 'email' => true, 'sms' => false];

        if ($deveEnviar && empty($canais['whatsapp']) && empty($canais['email']) && empty($canais['sms'])) {
            responder(['ok' => false, 'erro' => 'Nenhum canal de cobrança está habilitado. Configure em Mensagens > Canais.'], 422);
        }

        $hoje = new DateTime('today');
        $venc = new DateTime($clienteAlvo['vencimento'] ?? 'today');
        $venc->setTime(0, 0, 0);
        $diasRestantes = (int) $hoje->diff($venc)->format('%r%a');

        $mensagens = resolver_mensagens_para_usuario($clienteAlvo['criado_por'] ?? null);
        $chaveTemplate = $diasRestantes <= 0 ? 'aviso_vencimento_dia' : 'aviso_3_dias';
        $textoEscolhido = escolher_variacao_cliente($clienteAlvo, $mensagens[$chaveTemplate] ?? [], $chaveTemplate);
        $texto = montar_mensagem($textoEscolhido, $clienteAlvo, max(0, $diasRestantes));

        $statusEmail = ['habilitado' => !empty($canais['email']), 'tentado' => false, 'enfileirado' => false];
        $statusSms = ['habilitado' => !empty($canais['sms']), 'tentado' => false, 'enfileirado' => false];

        if ($deveEnviar) {
            if (!empty($canais['email']) && !empty($clienteAlvo['email'])) {
                $statusEmail['tentado'] = true;
                enfileirar_email($clienteAlvo['email'], 'Cobrança - ' . ($clienteAlvo['nome'] ?? ''), nl2br(htmlspecialchars($texto)), $clienteAlvo['id'] ?? null, $clienteAlvo['criado_por'] ?? null);
                $statusEmail['enfileirado'] = true;
            }
            if (!empty($canais['sms']) && !empty($clienteAlvo['whatsapp'])) {
                $statusSms['tentado'] = true;
                enfileirar_sms($clienteAlvo['whatsapp'], $texto, $clienteAlvo['id'] ?? null, $usuarioLogado['id']);
                $statusSms['enfileirado'] = true;
            }
        }

        $enviosAutomaticos = [];
        if ($statusEmail['enfileirado']) $enviosAutomaticos[] = 'email';
        if ($statusSms['enfileirado']) $enviosAutomaticos[] = 'sms';

        // "Cobrar" (deveEnviar=true) sempre conta como envio de verdade —
        // mesmo que só o WhatsApp (manual) tenha saído, o operador já viu
        // e vai mandar. "Copiar" (deveEnviar=false) nunca avança nada.
        if ($deveEnviar) {
            avancar_rotacao_cliente($clienteAlvo['id'], [$chaveTemplate, 'saudacao', 'despedida']);
        }

        responder([
            'ok' => true,
            'whatsapp' => !empty($canais['whatsapp']) ? $clienteAlvo['whatsapp'] : null,
            'texto' => $texto,
            'envios_automaticos' => $enviosAutomaticos,
            'canais_status' => [
                'whatsapp' => ['habilitado' => !empty($canais['whatsapp']) && !empty($clienteAlvo['whatsapp']), 'automatico' => false],
                'email' => $statusEmail,
                'sms' => $statusSms,
            ],
        ]);
        break;

    case 'limpar_inativos':
        $limite = new DateTime('-2 days');
        $removidos = 0;
        db_update('clientes', function (&$clientes) use ($limite, &$removidos) {
            $antes = count($clientes);
            $clientes = array_values(array_filter($clientes, function ($c) use ($limite) {
                if (($c['status'] ?? '') !== 'Inativo' || empty($c['vencimento'])) {
                    return true;
                }
                return new DateTime($c['vencimento']) > $limite;
            }));
            $removidos = $antes - count($clientes);
        });
        auditoria_log($usuarioLogado['usuario'], 'LIMPEZA_INATIVOS', "$removidos removidos");
        responder(['ok' => true, 'removidos' => $removidos]);
        break;

    default:
        responder(['ok' => false, 'erro' => 'Ação inválida.'], 400);
    }
} catch (\Throwable $e) {
    error_log('Erro inesperado em api/clientes.php (ação: ' . $acao . '): ' . $e->getMessage());
    if (!headers_sent()) {
        http_response_code(500);
    }
    echo json_encode(['ok' => false, 'erro' => 'Erro interno ao processar a solicitação. Verifique os logs do servidor.'], JSON_UNESCAPED_UNICODE);
}
