<?php

namespace Modules\APIPublica\Services;

use PDO;
use Exception;

/**
 * Serviço para gerenciamento de webhooks
 */
class WebhookService {
    private PDO $pdo;
    private string $tenantId;

    public function __construct(string $tenantId) {
        $this->tenantId = $tenantId;
        $this->pdo = \Gestoreasy\Core\Tenant\ConnectionManager::forTenant($tenantId);
    }

    /**
     * Dispara webhooks para um evento
     */
    public function disparar(string $evento, array $dados): void {
        // Buscar webhooks ativos para este evento
        $stmt = $this->pdo->prepare('
            SELECT * FROM webhooks
            WHERE tenant_id = ? AND ativo = TRUE
        ');
        $stmt->execute([$this->tenantId]);
        $webhooks = $stmt->fetchAll(PDO::FETCH_ASSOC);

        foreach ($webhooks as $webhook) {
            $eventos = json_decode($webhook['eventos_json'], true) ?? [];
            
            // Verificar se webhook escuta este evento
            if (in_array($evento, $eventos) || in_array('*', $eventos)) {
                $this->executarWebhook($webhook['id'], $evento, $dados);
            }
        }
    }

    /**
     * Executa um webhook
     */
    private function executarWebhook(int $webhookId, string $evento, array $dados): void {
        // Buscar webhook
        $stmt = $this->pdo->prepare('SELECT * FROM webhooks WHERE id = ? AND tenant_id = ?');
        $stmt->execute([$webhookId, $this->tenantId]);
        $webhook = $stmt->fetch(PDO::FETCH_ASSOC);

        if (!$webhook) {
            return;
        }

        // Criar payload
        $payload = [
            'event' => $evento,
            'timestamp' => date('c'),
            'data' => $dados
        ];

        // Assinar payload se houver secret
        if (!empty($webhook['secret'])) {
            $payload['signature'] = $this->assinarPayload($payload, $webhook['secret']);
        }

        // Criar registro de execução
        $executionId = $this->criarExecucao($webhookId, $evento, $payload);

        // Enviar webhook (assíncrono via queue)
        $this->enviarWebhook($webhook, $payload, $executionId);
    }

    /**
     * Envia webhook HTTP
     */
    private function enviarWebhook(array $webhook, array $payload, int $executionId): void {
        $url = $webhook['url'];
        $metodo = $webhook['metodo_http'] ?? 'POST';
        $headers = json_decode($webhook['headers_json'] ?? '{}', true);
        
        // Headers padrão
        $headers['Content-Type'] = 'application/json';
        $headers['User-Agent'] = 'GestorEasy-Webhook/1.0';

        // Preparar headers para cURL
        $curlHeaders = [];
        foreach ($headers as $key => $value) {
            $curlHeaders[] = "{$key}: {$value}";
        }

        // Atualizar status para "enviando"
        $stmt = $this->pdo->prepare('
            UPDATE webhook_executions
            SET status = "enviando", iniciado_em = NOW()
            WHERE id = ?
        ');
        $stmt->execute([$executionId]);

        // Enviar requisição
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_CUSTOMREQUEST => $metodo,
            CURLOPT_POSTFIELDS => json_encode($payload),
            CURLOPT_HTTPHEADER => $curlHeaders,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_CONNECTTIMEOUT => 10,
        ]);

        $resposta = curl_exec($ch);
        $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $erro = curl_error($ch);
        curl_close($ch);

        // Atualizar execução
        $status = ($statusCode >= 200 && $statusCode < 300) ? 'sucesso' : 'falha';
        
        $stmt = $this->pdo->prepare('
            UPDATE webhook_executions
            SET status = ?,
                status_code = ?,
                resposta = ?,
                finalizado_em = NOW()
            WHERE id = ?
        ');
        $stmt->execute([
            $status,
            $statusCode,
            $erro ?: substr($resposta, 0, 1000),
            $executionId
        ]);

        // Atualizar estatísticas do webhook
        if ($status === 'sucesso') {
            $stmt = $this->pdo->prepare('
                UPDATE webhooks
                SET total_envios = total_envios + 1,
                    total_sucessos = total_sucessos + 1,
                    ultimo_envio = NOW()
                WHERE id = ?
            ');
        } else {
            $stmt = $this->pdo->prepare('
                UPDATE webhooks
                SET total_envios = total_envios + 1,
                    total_falhas = total_falhas + 1,
                    ultimo_envio = NOW()
                WHERE id = ?
            ]);
            
            // Agendar retry se necessário
            $this->agendarRetry($executionId, $webhook);
        }
        $stmt->execute([$webhook['id']]);
    }

    /**
     * Agenda retry de webhook
     */
    private function agendarRetry(int $executionId, array $webhook): void {
        $stmt = $this->pdo->prepare('
            SELECT tentativa_atual FROM webhook_executions WHERE id = ?
        ');
        $stmt->execute([$executionId]);
        $exec = $stmt->fetch(PDO::FETCH_ASSOC);

        $tentativaAtual = (int)($exec['tentativa_atual'] ?? 1);
        $maxTentativas = (int)($webhook['max_tentativas'] ?? 3);

        if ($tentativaAtual < $maxTentativas) {
            $intervalo = (int)($webhook['intervalo_retry_segundos'] ?? 60);
            $proximaTentativa = date('Y-m-d H:i:s', strtotime("+{$intervalo} seconds"));

            $stmt = $this->pdo->prepare('
                UPDATE webhook_executions
                SET status = "pendente",
                    tentativa_atual = tentativa_atual + 1,
                    proxima_tentativa = ?
                WHERE id = ?
            ');
            $stmt->execute([$proximaTentativa, $executionId]);
        }
    }

    /**
     * Cria registro de execução
     */
    private function criarExecucao(int $webhookId, string $evento, array $payload): int {
        $stmt = $this->pdo->prepare('
            INSERT INTO webhook_executions (
                tenant_id, webhook_id, evento, payload_json, status
            ) VALUES (?, ?, ?, ?, "pendente")
        ');
        $stmt->execute([
            $this->tenantId,
            $webhookId,
            $evento,
            json_encode($payload)
        ]);

        return (int)$this->pdo->lastInsertId();
    }

    /**
     * Assina payload com secret
     */
    private function assinarPayload(array $payload, string $secret): string {
        $payloadStr = json_encode($payload);
        return hash_hmac('sha256', $payloadStr, $secret);
    }

    /**
     * Processa retries pendentes
     */
    public function processarRetries(): void {
        $stmt = $this->pdo->prepare('
            SELECT we.*, w.url, w.metodo_http, w.headers_json, w.secret, w.max_tentativas, w.intervalo_retry_segundos
            FROM webhook_executions we
            INNER JOIN webhooks w ON we.webhook_id = w.id
            WHERE we.tenant_id = ?
            AND we.status = "pendente"
            AND we.proxima_tentativa <= NOW()
            LIMIT 100
        ');
        $stmt->execute([$this->tenantId]);
        $execucoes = $stmt->fetchAll(PDO::FETCH_ASSOC);

        foreach ($execucoes as $exec) {
            $payload = json_decode($exec['payload_json'], true);
            $this->enviarWebhook($exec, $payload, $exec['id']);
        }
    }
}

