<?php

declare(strict_types=1);

class WhatsAppService
{
    private $configModel;
    private $logModel;
    private $dispatchModel;

    public function __construct()
    {
        $this->configModel = new WhatsAppConfig();
        $this->logModel = new WhatsAppLog();
        $this->dispatchModel = new WhatsAppDispatch();
    }

    public function config(): array
    {
        return $this->configModel->get();
    }

    public function isActive(): bool
    {
        $config = $this->config();
        if ((int) ($config['ativo'] ?? 0) !== 1) {
            return false;
        }

        $mode = (string) ($config['integration_mode'] ?? 'waychat');
        if ($mode === 'waychat') {
            return trim((string) ($config['auth_token'] ?? '')) !== '';
        }

        return trim((string) ($config['webhook_url'] ?? '')) !== '';
    }

    public function webhookTokenMatches(?string $token): bool
    {
        $config = $this->config();
        $expected = trim((string) ($config['webhook_token'] ?? ''));
        return $expected !== '' && $token !== null && hash_equals($expected, trim($token));
    }

    public function latestLogs(int $limit = 30): array
    {
        return $this->logModel->latest($limit);
    }

    public function outboundPayloadPreview(): array
    {
        $config = $this->config();
        $mensagem = "Olá JOÃO DA SILVA, recebemos sua solicitação.\n\nProtocolo: PR-2026-000001\nServiço: BURACO NA VIA\nAcompanhe: " . base_url('/consultar?protocolo=PR-2026-000001');

        if (($config['integration_mode'] ?? 'waychat') === 'waychat') {
            return $this->buildWayChatPayload('5535999999999', $mensagem, 'nova_ocorrencia', [
                'protocolo' => 'PR-2026-000001',
                'categoria_nome' => 'BURACO NA VIA',
                'descricao' => 'Buraco grande em frente à escola municipal.',
                'endereco_referencia' => 'Rua Central, próximo ao nº 120',
                'cidadao_nome' => 'JOÃO DA SILVA',
                'cidadao_telefone' => '5535999999999',
                'status' => 'ABERTA',
            ]);
        }

        return $this->buildCrmPayload('nova_ocorrencia', '5535999999999', $mensagem, []);
    }

    public function sendTest(string $telefone, string $mensagem): array
    {
        return $this->sendRaw('teste_manual', $telefone, $mensagem, [
            'evento' => 'teste_manual',
            'protocolo' => 'TESTE-CRM',
            'status' => 'TESTE',
            'categoria_nome' => 'TESTE',
            'cidadao_nome' => 'CONTATO TESTE',
            'cidadao_telefone' => $telefone,
            'descricao' => 'Teste manual do painel Prefeitura Resolve.',
            'endereco_referencia' => 'Painel administrativo',
            'origem_canal' => 'PAINEL',
        ]);
    }

    public function notifyNewOccurrence(array $ocorrencia): ?array
    {
        $config = $this->config();
        if ((int) ($config['auto_notify_new'] ?? 0) !== 1) {
            return null;
        }

        $telefone = $this->normalizePhone((string) ($ocorrencia['cidadao_telefone'] ?? ''));
        if ($telefone === '') {
            return null;
        }

        $mensagem = $this->renderTemplate((string) $config['template_nova_ocorrencia'], $ocorrencia);
        return $this->sendEvent('nova_ocorrencia', $telefone, $mensagem, $ocorrencia, (string) ($ocorrencia['protocolo'] ?? ''));
    }

    public function notifyStatusChange(array $ocorrencia): ?array
    {
        $config = $this->config();
        if ((int) ($config['auto_notify_status'] ?? 0) !== 1) {
            return null;
        }

        $telefone = $this->normalizePhone((string) ($ocorrencia['cidadao_telefone'] ?? ''));
        if ($telefone === '') {
            return null;
        }

        $mensagem = $this->renderTemplate((string) $config['template_status'], $ocorrencia);
        return $this->sendEvent('alteracao_status', $telefone, $mensagem, $ocorrencia, (string) ($ocorrencia['status'] ?? ''));
    }

    public function processSlaAlerts(): array
    {
        $config = $this->config();
        $result = ['enviados' => 0, 'ignorados' => 0];

        if ((int) ($config['auto_notify_sla'] ?? 0) !== 1) {
            return $result;
        }

        $model = new Ocorrencia();
        $items = $model->listarCriticas(100);

        foreach ($items as $item) {
            $telefone = $this->normalizePhone((string) ($item['cidadao_telefone'] ?? ''));
            if ($telefone === '') {
                $result['ignorados']++;
                continue;
            }

            $reference = date('Y-m-d') . ':' . (string) ($item['sla_status'] ?? 'ALERTA');
            if ($this->dispatchModel->alreadySent((int) $item['id'], 'sla_alerta', $reference)) {
                $result['ignorados']++;
                continue;
            }

            $mensagem = $this->renderTemplate((string) $config['template_sla'], $item);
            $send = $this->sendEvent('sla_vencido', $telefone, $mensagem, $item, $reference);
            if (($send['success'] ?? false) === true) {
                $result['enviados']++;
            } else {
                $result['ignorados']++;
            }
        }

        return $result;
    }

    public function sendEvent(string $event, string $telefone, string $mensagem, array $ocorrencia = [], string $reference = ''): array
    {
        $payload = $this->buildCrmPayload($event, $telefone, $mensagem, $ocorrencia);
        $response = $this->sendRaw($event, $telefone, $mensagem, $payload);

        if (($response['success'] ?? false) === true && $event === 'nova_ocorrencia') {
            $dealResponse = $this->createWayChatDeal($telefone, $ocorrencia);
            if ($dealResponse !== null) {
                $response['deal'] = $dealResponse;
            }
        }

        if (!empty($ocorrencia['id'])) {
            $this->dispatchModel->add([
                'ocorrencia_id' => (int) $ocorrencia['id'],
                'gatilho' => $event,
                'referencia' => $reference !== '' ? $reference : date('Y-m-d H:i:s'),
                'telefone' => $telefone,
                'mensagem' => $mensagem,
                'payload_json' => json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
                'response_json' => json_encode($response, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
                'status_code' => (int) ($response['status_code'] ?? 0),
                'sucesso' => !empty($response['success']) ? 1 : 0,
            ]);
        }

        return $response;
    }

    public function handleWebhook(array $payload): array
    {
        $this->logInbound($payload, 'webhook_entrada', '', null, 200, true, 'Webhook recebido');

        $acao = strtolower(trim((string) ($payload['acao'] ?? '')));
        if ($acao === '') {
            $acao = $this->inferAction($payload);
        }

        if ($acao === 'consultar_protocolo') {
            $protocolo = trim((string) ($payload['protocolo'] ?? $this->extractProtocol((string) ($payload['mensagem'] ?? ''))));
            if ($protocolo === '') {
                return ['success' => false, 'message' => 'Protocolo não informado.'];
            }

            $model = new Ocorrencia();
            $item = $model->buscarPorProtocolo($protocolo);
            if (!$item) {
                return ['success' => false, 'message' => 'Protocolo não encontrado.'];
            }

            return [
                'success' => true,
                'message' => 'Consulta realizada com sucesso.',
                'data' => [
                    'protocolo' => $item['protocolo'],
                    'status' => $item['status'],
                    'servico' => $item['categoria_nome'] ?? '',
                    'descricao' => $item['descricao'] ?? '',
                    'link_consulta' => base_url('/consultar?protocolo=' . urlencode((string) $item['protocolo'])),
                ],
            ];
        }

        if ($acao === 'abrir_ocorrencia') {
            $config = $this->config();
            if ((int) ($config['auto_create_from_webhook'] ?? 0) !== 1) {
                return ['success' => false, 'message' => 'Criação automática via webhook desativada.'];
            }

            foreach (['categoria_id', 'descricao'] as $field) {
                if (!isset($payload[$field]) || trim((string) $payload[$field]) === '') {
                    return ['success' => false, 'message' => 'Campo obrigatório: ' . $field];
                }
            }

            $db = Database::getConnection();
            $protocolo = gerarProtocolo($db);
            $model = new Ocorrencia();
            $id = $model->criar([
                'protocolo' => $protocolo,
                'categoria_id' => (int) $payload['categoria_id'],
                'descricao' => trim((string) $payload['descricao']),
                'latitude' => $this->nullableString($payload['latitude'] ?? null),
                'longitude' => $this->nullableString($payload['longitude'] ?? null),
                'endereco_referencia' => trim((string) ($payload['endereco_referencia'] ?? $payload['endereco'] ?? $payload['logradouro'] ?? '')),
                'cidadao_nome' => trim((string) ($payload['cidadao_nome'] ?? $payload['nome'] ?? '')),
                'cidadao_telefone' => $this->normalizePhone((string) ($payload['cidadao_telefone'] ?? $payload['telefone'] ?? '')),
                'cidadao_email' => trim((string) ($payload['cidadao_email'] ?? $payload['email'] ?? '')),
                'anonima' => (int) ($payload['anonima'] ?? 0),
                'imagem' => null,
            ]);

            $item = $model->buscarPorId($id);
            if (is_array($item)) {
                $this->notifyNewOccurrence($item);
            }

            return [
                'success' => true,
                'message' => 'Ocorrência criada com sucesso.',
                'data' => [
                    'id' => $id,
                    'protocolo' => $protocolo,
                    'status' => 'ABERTA',
                    'url_consulta' => base_url('/consultar?protocolo=' . urlencode($protocolo)),
                ],
            ];
        }

        return ['success' => false, 'message' => 'Ação não reconhecida. Use abrir_ocorrencia ou consultar_protocolo.'];
    }

    public function renderTemplate(string $template, array $ocorrencia): string
    {
        $map = [
            '{nome}' => trim((string) ($ocorrencia['cidadao_nome'] ?? 'Cidadão')),
            '{protocolo}' => (string) ($ocorrencia['protocolo'] ?? ''),
            '{status}' => (string) ($ocorrencia['status'] ?? ''),
            '{servico}' => (string) ($ocorrencia['categoria_nome'] ?? ''),
            '{descricao}' => (string) ($ocorrencia['descricao'] ?? ''),
            '{telefone}' => (string) ($ocorrencia['cidadao_telefone'] ?? ''),
            '{sla_label}' => (string) ($ocorrencia['sla_prazo_texto'] ?? ($ocorrencia['sla_status'] ?? '')),
            '{link_consulta}' => base_url('/consultar?protocolo=' . urlencode((string) ($ocorrencia['protocolo'] ?? ''))),
        ];

        return trim(strtr($template, $map));
    }

    private function buildCrmPayload(string $event, string $telefone, string $mensagem, array $ocorrencia): array
    {
        $protocolo = (string) ($ocorrencia['protocolo'] ?? '');
        $status = (string) ($ocorrencia['status'] ?? '');
        $servico = (string) ($ocorrencia['categoria_nome'] ?? $ocorrencia['servico'] ?? '');
        $nome = trim((string) ($ocorrencia['cidadao_nome'] ?? $ocorrencia['nome'] ?? 'CIDADÃO'));
        $email = trim((string) ($ocorrencia['cidadao_email'] ?? $ocorrencia['email'] ?? ''));
        $linkConsulta = $protocolo !== '' ? base_url('/consultar?protocolo=' . urlencode($protocolo)) : base_url('/consultar');
        $tagEvento = 'prefeitura_resolve_' . $event;

        return [
            'origem' => 'prefeitura_resolve',
            'sistema' => 'Prefeitura Resolve',
            'evento' => $event,
            'tag' => $tagEvento,
            'nome' => $nome,
            'telefone' => $telefone,
            'email' => $email,
            'mensagem' => $mensagem,
            'protocolo' => $protocolo,
            'status' => $status,
            'servico' => $servico,
            'descricao' => (string) ($ocorrencia['descricao'] ?? ''),
            'endereco_referencia' => (string) ($ocorrencia['endereco_referencia'] ?? ''),
            'latitude' => (string) ($ocorrencia['latitude'] ?? ''),
            'longitude' => (string) ($ocorrencia['longitude'] ?? ''),
            'sla_label' => (string) ($ocorrencia['sla_prazo_texto'] ?? ($ocorrencia['sla_status'] ?? '')),
            'url_consulta' => $linkConsulta,
            'origem_canal' => (string) ($ocorrencia['origem_canal'] ?? 'SISTEMA'),
            'data_evento' => date('c'),
            'custom_fields' => [
                'protocolo' => $protocolo,
                'servico' => $servico,
                'status' => $status,
                'url_consulta' => $linkConsulta,
                'endereco_referencia' => (string) ($ocorrencia['endereco_referencia'] ?? ''),
                'latitude' => (string) ($ocorrencia['latitude'] ?? ''),
                'longitude' => (string) ($ocorrencia['longitude'] ?? ''),
                'origem' => 'prefeitura_resolve',
            ],
            'payload' => [
                'ocorrencia_id' => isset($ocorrencia['id']) ? (int) $ocorrencia['id'] : null,
                'categoria_id' => isset($ocorrencia['categoria_id']) ? (int) $ocorrencia['categoria_id'] : null,
                'telefone_remetente' => (string) (($this->config()['telefone_remetente'] ?? '') ?: ''),
            ],
        ];
    }

    private function buildWayChatPayload(string $telefone, string $mensagem, string $event, array $ocorrencia): array
    {
        $config = $this->config();
        $status = trim((string) ($config['waychat_status_on_send'] ?? ''));
        $payload = [
            'to' => $telefone,
            'text' => $mensagem,
        ];

        if (($config['waychat_send_mode'] ?? 'message') === 'template') {
            $templateName = trim((string) ($config['waychat_template_name'] ?? ''));
            if ($templateName !== '') {
                $payload = [
                    'to' => $telefone,
                    'template' => [
                        'name' => $templateName,
                        'language' => ['code' => trim((string) ($config['waychat_template_language'] ?? 'pt_BR')) ?: 'pt_BR'],
                        'components' => [[
                            'type' => 'body',
                            'parameters' => [
                                ['type' => 'text', 'text' => $this->renderTemplate((string) $config['template_nova_ocorrencia'], $ocorrencia)],
                            ],
                        ]],
                    ],
                ];
            }
        }

        if ($status !== '') {
            $payload['status'] = $status;
        }

        return $payload;
    }

    private function sendRaw(string $event, string $telefone, string $mensagem, array $payload): array
    {
        $config = $this->config();
        if (!$this->isActive()) {
            $response = ['success' => false, 'message' => 'Integração WhatsApp desativada.', 'status_code' => 0];
            $this->logOutbound($event, $telefone, $payload['protocolo'] ?? null, $payload, $response, 0, false, $response['message']);
            return $response;
        }

        $mode = (string) ($config['integration_mode'] ?? 'waychat');
        if ($mode === 'waychat') {
            return $this->sendWayChat($event, $telefone, $mensagem, $payload);
        }

        return $this->sendGenericWebhook($event, $telefone, $payload);
    }

    private function sendWayChat(string $event, string $telefone, string $mensagem, array $payload): array
    {
        $config = $this->config();
        $base = rtrim(trim((string) ($config['waychat_base_url'] ?? 'https://waychat.app.br')), '/');
        $connectionType = (string) ($config['waychat_connection_type'] ?? 'cloud');
        $sendMode = (string) ($config['waychat_send_mode'] ?? 'message');
        $endpoint = $sendMode === 'template'
            ? $base . '/api/v1/send-template'
            : ($connectionType === 'lite' ? $base . '/api/v1/uazapi/send-message' : $base . '/api/v1/send-message');

        $requestBody = $this->buildWayChatPayload($telefone, $mensagem, $event, $payload);
        $response = $this->postJson($endpoint, $requestBody, trim((string) ($config['auth_token'] ?? '')));
        $this->logOutbound($event, $telefone, $payload['protocolo'] ?? null, $requestBody, $response, (int) ($response['status_code'] ?? 0), !empty($response['success']), !empty($response['success']) ? 'Mensagem enviada ao WayChat com sucesso.' : (($response['error'] ?? '') ?: 'Falha no envio ao WayChat.'));
        return $response;
    }

    private function sendGenericWebhook(string $event, string $telefone, array $payload): array
    {
        $config = $this->config();
        $endpoint = trim((string) $config['webhook_url']);
        $response = $this->postJson($endpoint, $payload, trim((string) ($config['auth_token'] ?? '')));
        $this->logOutbound($event, $telefone, $payload['protocolo'] ?? null, $payload, $response, (int) ($response['status_code'] ?? 0), !empty($response['success']), !empty($response['success']) ? 'Webhook CRM recebido com sucesso.' : (($response['error'] ?? '') ?: 'Falha no envio ao CRM.'));
        return $response;
    }

    private function createWayChatDeal(string $telefone, array $ocorrencia): ?array
    {
        $config = $this->config();
        if (($config['integration_mode'] ?? 'waychat') !== 'waychat' || (int) ($config['waychat_create_deal'] ?? 0) !== 1) {
            return null;
        }

        $companyToken = trim((string) ($config['company_auth_token'] ?? ''));
        $sectorName = trim((string) ($config['waychat_sector_name'] ?? ''));
        $columnName = trim((string) ($config['waychat_column_name'] ?? ''));
        if ($companyToken === '' || $sectorName === '' || $columnName === '') {
            return ['success' => false, 'message' => 'Criação de deal ignorada: informe API Key da empresa, pipeline/setor e coluna.'];
        }

        $base = rtrim(trim((string) ($config['waychat_base_url'] ?? 'https://waychat.app.br')), '/');
        $endpoint = $base . '/api/v1/deals/create';
        $requestBody = [
            'contact' => [
                'phone' => $telefone,
                'name' => trim((string) ($ocorrencia['cidadao_nome'] ?? 'CIDADÃO')),
                'email' => trim((string) ($ocorrencia['cidadao_email'] ?? '')),
                'notes' => 'Contato criado automaticamente pelo Prefeitura Resolve.',
            ],
            'kanban' => [
                'sector_name' => $sectorName,
                'column_name' => $columnName,
            ],
            'deal' => [
                'lead_source' => 'Prefeitura Resolve',
            ],
            'notes' => array_values(array_filter([
                'Protocolo: ' . (string) ($ocorrencia['protocolo'] ?? ''),
                'Serviço: ' . (string) ($ocorrencia['categoria_nome'] ?? ''),
                'Status: ' . (string) ($ocorrencia['status'] ?? 'ABERTA'),
                'Descrição: ' . (string) ($ocorrencia['descricao'] ?? ''),
                'Endereço: ' . (string) ($ocorrencia['endereco_referencia'] ?? ''),
                'Link de consulta: ' . base_url('/consultar?protocolo=' . urlencode((string) ($ocorrencia['protocolo'] ?? ''))),
            ])),
        ];

        $response = $this->postJson($endpoint, $requestBody, $companyToken);
        $this->logOutbound('waychat_deal', $telefone, $ocorrencia['protocolo'] ?? null, $requestBody, $response, (int) ($response['status_code'] ?? 0), !empty($response['success']), !empty($response['success']) ? 'Deal criado/atualizado no WayChat.' : (($response['error'] ?? '') ?: 'Falha ao criar deal no WayChat.'));
        return $response;
    }

    private function postJson(string $url, array $requestBody, string $bearerToken): array
    {
        $headers = [
            'Content-Type: application/json',
            'Accept: application/json',
        ];
        if ($bearerToken !== '') {
            $headers[] = 'Authorization: Bearer ' . $bearerToken;
        }

        $json = json_encode($requestBody, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        $statusCode = 0;
        $body = '';
        $error = '';

        if (function_exists('curl_init')) {
            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_TIMEOUT, 20);
            $body = (string) curl_exec($ch);
            if (curl_errno($ch)) {
                $error = (string) curl_error($ch);
            }
            $statusCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
        } else {
            $context = stream_context_create([
                'http' => [
                    'method' => 'POST',
                    'header' => implode("
", $headers),
                    'content' => $json,
                    'timeout' => 20,
                    'ignore_errors' => true,
                ],
            ]);
            $body = (string) @file_get_contents($url, false, $context);
            $statusCode = 200;
        }

        $decodedBody = json_decode($body, true);
        return [
            'success' => $error === '' && $statusCode >= 200 && $statusCode < 300,
            'status_code' => $statusCode,
            'body' => $body,
            'body_json' => is_array($decodedBody) ? $decodedBody : null,
            'error' => $error,
        ];
    }

    private function logOutbound(string $event, string $telefone, ?string $protocolo, array $requestBody, array $responseBody, int $statusCode, bool $success, string $message): void
    {
        $this->logModel->add([
            'direction' => 'OUT',
            'evento' => $event,
            'telefone' => $telefone,
            'protocolo' => $protocolo,
            'payload_json' => json_encode($requestBody, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
            'response_json' => json_encode($responseBody, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
            'status_code' => $statusCode,
            'sucesso' => $success ? 1 : 0,
            'mensagem' => $message,
        ]);
    }

    private function logInbound(array $payload, string $event, string $telefone, ?string $protocolo, int $statusCode, bool $success, string $message): void
    {
        $this->logModel->add([
            'direction' => 'IN',
            'evento' => $event,
            'telefone' => $telefone,
            'protocolo' => $protocolo,
            'payload_json' => json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
            'response_json' => null,
            'status_code' => $statusCode,
            'sucesso' => $success ? 1 : 0,
            'mensagem' => $message,
        ]);
    }

    private function inferAction(array $payload): string
    {
        if (!empty($payload['protocolo'])) {
            return 'consultar_protocolo';
        }

        $mensagem = strtoupper(trim((string) ($payload['mensagem'] ?? '')));
        if (strpos($mensagem, 'CONSULTAR') === 0 || preg_match('/PR\-\d{4}\-\d+/i', $mensagem)) {
            return 'consultar_protocolo';
        }

        return 'abrir_ocorrencia';
    }

    private function extractProtocol(string $text): string
    {
        if (preg_match('/(PR\-\d{4}\-\d+)/i', $text, $m)) {
            return strtoupper($m[1]);
        }
        return '';
    }

    private function normalizePhone(string $phone): string
    {
        return preg_replace('/\D+/', '', $phone);
    }

    private function nullableString($value): ?string
    {
        $value = is_string($value) ? trim($value) : '';
        return $value === '' ? null : $value;
    }
}
