<?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();
        return (int) ($config['ativo'] ?? 0) === 1 && 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 sendTest(string $telefone, string $mensagem): array
    {
        return $this->sendRaw('teste', $telefone, $mensagem, [
            'evento' => 'teste_manual',
        ]);
    }

    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('status_atualizado', $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_alerta', $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 = [
            'evento' => $event,
            'telefone' => $telefone,
            'mensagem' => $mensagem,
            'protocolo' => $ocorrencia['protocolo'] ?? null,
            'ocorrencia' => $ocorrencia,
        ];

        $response = $this->sendRaw($event, $telefone, $mensagem, $payload);

        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', 'latitude', 'longitude'] as $field) {
                if (!isset($payload[$field]) || $payload[$field] === '' || $payload[$field] === null) {
                    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' => (string) $payload['latitude'],
                'longitude' => (string) $payload['longitude'],
                'endereco_referencia' => trim((string) ($payload['endereco_referencia'] ?? '')), 
                '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'] ?? '')),
                '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',
                ],
            ];
        }

        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'] ?? ''),
            '{link_consulta}' => base_url('/consultar?protocolo=' . urlencode((string) ($ocorrencia['protocolo'] ?? ''))),
        ];

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

    private function sendRaw(string $event, string $telefone, string $mensagem, array $payload): array
    {
        $config = $this->config();
        $requestBody = [
            'provider' => (string) ($config['provider_name'] ?? 'Webhook genérico'),
            'from' => (string) ($config['telefone_remetente'] ?? ''),
            'to' => $telefone,
            'message' => $mensagem,
            'event' => $event,
            'payload' => $payload,
        ];

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

        $url = trim((string) $config['webhook_url']);
        $headers = [
            'Content-Type: application/json',
        ];
        $authToken = trim((string) ($config['auth_token'] ?? ''));
        if ($authToken !== '') {
            $headers[] = 'Authorization: Bearer ' . $authToken;
        }

        $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("\r\n", $headers),
                    'content' => $json,
                    'timeout' => 20,
                    'ignore_errors' => true,
                ],
            ]);
            $body = (string) @file_get_contents($url, false, $context);
            $statusCode = 200;
        }

        $success = $error === '' && $statusCode >= 200 && $statusCode < 300;
        $response = [
            'success' => $success,
            'status_code' => $statusCode,
            'body' => $body,
            'error' => $error,
        ];

        $this->logOutbound($event, $telefone, $payload['protocolo'] ?? null, $requestBody, $response, $statusCode, $success, $success ? 'Envio processado.' : ($error !== '' ? $error : 'Falha no envio.'));

        return $response;
    }

    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);
    }
}
