<?php

namespace Modules\CRMAvancado\Services;

use PDO;
use Exception;
use Gestoreasy\Core\Tenant\ConnectionManager;
use Gestoreasy\Core\Mail\Mailer;

/**
 * Serviço para envio de e-mails de marketing
 */
class EmailMarketingService {
    private PDO $pdo;
    private string $tenantId;
    private array $config;

    public function __construct(string $tenantId) {
        $this->tenantId = $tenantId;
        $this->pdo = ConnectionManager::forTenant($tenantId);
        $this->config = $this->getConfig();
    }

    /**
     * Obtém configuração de e-mail
     */
    private function getConfig(): array {
        // Buscar configuração do tenant (pode vir de uma tabela de configurações)
        // Por enquanto, usar configurações globais
        return [
            'smtp_host' => getenv('SMTP_HOST') ?: 'localhost',
            'smtp_port' => (int)(getenv('SMTP_PORT') ?: 587),
            'smtp_user' => getenv('SMTP_USER') ?: '',
            'smtp_pass' => getenv('SMTP_PASS') ?: '',
            'smtp_from' => getenv('SMTP_FROM') ?: 'noreply@gestoreasy.com',
            'smtp_from_name' => getenv('SMTP_FROM_NAME') ?: 'GestorEasy',
            'track_opens' => true,
            'track_clicks' => true,
            'unsubscribe_url' => getenv('BASE_URL') . '/unsubscribe'
        ];
    }

    /**
     * Envia campanha de e-mail
     */
    public function enviarCampanha(int $campanhaId): array {
        // Buscar campanha
        $stmt = $this->pdo->prepare('SELECT * FROM email_campaigns WHERE id = ? AND tenant_id = ?');
        $stmt->execute([$campanhaId, $this->tenantId]);
        $campanha = $stmt->fetch(PDO::FETCH_ASSOC);

        if (!$campanha) {
            throw new Exception('Campanha não encontrada');
        }

        if ($campanha['status'] !== 'rascunho' && $campanha['status'] !== 'agendada') {
            throw new Exception('Campanha já foi enviada ou está em outro status');
        }

        // Atualizar status para "enviando"
        $stmt = $this->pdo->prepare('UPDATE email_campaigns SET status = "enviando" WHERE id = ?');
        $stmt->execute([$campanhaId]);

        try {
            // Buscar destinatários
            $destinatarios = $this->buscarDestinatarios($campanha);

            // Criar registros de destinatários se não existirem
            $this->criarDestinatarios($campanhaId, $destinatarios);

            // Enviar e-mails
            $resultado = $this->enviarEmails($campanhaId, $campanha, $destinatarios);

            // Atualizar estatísticas da campanha
            $this->atualizarEstatisticasCampanha($campanhaId, $resultado);

            // Atualizar status para "enviada"
            $stmt = $this->pdo->prepare('UPDATE email_campaigns SET status = "enviada" WHERE id = ?');
            $stmt->execute([$campanhaId]);

            return $resultado;
        } catch (Exception $e) {
            // Atualizar status para erro
            $stmt = $this->pdo->prepare('UPDATE email_campaigns SET status = "rascunho" WHERE id = ?');
            $stmt->execute([$campanhaId]);
            throw $e;
        }
    }

    /**
     * Busca destinatários da campanha
     */
    private function buscarDestinatarios(array $campanha): array {
        $tipo = $campanha['segmento_tipo'];
        $filtros = !empty($campanha['segmento_filtros_json']) 
            ? json_decode($campanha['segmento_filtros_json'], true) 
            : [];

        $destinatarios = [];

        switch ($tipo) {
            case 'todos':
                // Buscar todos os clientes com e-mail
                try {
                    $stmt = $this->pdo->prepare('
                        SELECT email, nome, id as cliente_id, NULL as lead_id
                        FROM clients
                        WHERE email IS NOT NULL AND email != "" AND (inativo = 0 OR inativo IS NULL)
                    ');
                    $stmt->execute();
                    $clientes = $stmt->fetchAll(PDO::FETCH_ASSOC);
                    
                    // Tentar buscar leads se a tabela existir
                    try {
                        $stmt = $this->pdo->prepare('
                            SELECT email, nome, NULL as cliente_id, id as lead_id
                            FROM leads
                            WHERE email IS NOT NULL AND email != ""
                        ');
                        $stmt->execute();
                        $leads = $stmt->fetchAll(PDO::FETCH_ASSOC);
                        $destinatarios = array_merge($clientes, $leads);
                    } catch (Exception $e) {
                        // Tabela leads não existe, usar apenas clientes
                        $destinatarios = $clientes;
                    }
                } catch (Exception $e) {
                    $destinatarios = [];
                }
                break;

            case 'clientes':
                try {
                    $stmt = $this->pdo->prepare('
                        SELECT email, nome, id as cliente_id, NULL as lead_id
                        FROM clients
                        WHERE email IS NOT NULL AND email != "" AND (inativo = 0 OR inativo IS NULL)
                    ');
                    $stmt->execute();
                    $destinatarios = $stmt->fetchAll(PDO::FETCH_ASSOC);
                } catch (Exception $e) {
                    $destinatarios = [];
                }
                break;

            case 'leads':
                try {
                    $stmt = $this->pdo->prepare('
                        SELECT email, nome, NULL as cliente_id, id as lead_id
                        FROM leads
                        WHERE email IS NOT NULL AND email != ""
                    ');
                    $stmt->execute();
                    $destinatarios = $stmt->fetchAll(PDO::FETCH_ASSOC);
                } catch (Exception $e) {
                    // Tabela leads não existe
                    $destinatarios = [];
                }
                break;

            case 'customizado':
                // Aplicar filtros customizados
                $destinatarios = $this->buscarDestinatariosCustomizados($filtros);
                break;
        }

        return $destinatarios;
    }

    /**
     * Busca destinatários com filtros customizados
     */
    private function buscarDestinatariosCustomizados(array $filtros): array {
        $destinatarios = [];

        // Exemplo de filtros: cidade, estado, data_cadastro, etc.
        if (isset($filtros['tipo']) && $filtros['tipo'] === 'clientes') {
            $where = ['email IS NOT NULL AND email != "" AND inativo = 0'];
            $params = [];

            if (!empty($filtros['cidade'])) {
                $where[] = 'cidade = ?';
                $params[] = $filtros['cidade'];
            }

            if (!empty($filtros['estado'])) {
                $where[] = 'estado = ?';
                $params[] = $filtros['estado'];
            }

            $sql = 'SELECT email, nome, id as cliente_id, NULL as lead_id FROM clients WHERE ' . implode(' AND ', $where);
            $stmt = $this->pdo->prepare($sql);
            $stmt->execute($params);
            $destinatarios = $stmt->fetchAll(PDO::FETCH_ASSOC);
        }

        return $destinatarios;
    }

    /**
     * Cria registros de destinatários
     */
    private function criarDestinatarios(int $campanhaId, array $destinatarios): void {
        $stmt = $this->pdo->prepare('
            INSERT INTO email_campaign_recipients (
                tenant_id, campaign_id, email, nome, lead_id, cliente_id, status
            ) VALUES (?, ?, ?, ?, ?, ?, "pendente")
            ON DUPLICATE KEY UPDATE email = VALUES(email)
        ');

        foreach ($destinatarios as $dest) {
            $stmt->execute([
                $this->tenantId,
                $campanhaId,
                $dest['email'],
                $dest['nome'] ?? null,
                $dest['lead_id'] ?? null,
                $dest['cliente_id'] ?? null
            ]);
        }
    }

    /**
     * Envia e-mails da campanha
     */
    private function enviarEmails(int $campanhaId, array $campanha, array $destinatarios): array {
        $enviados = 0;
        $erros = 0;
        $rejeitados = 0;

        // Buscar destinatários pendentes
        $stmt = $this->pdo->prepare('
            SELECT * FROM email_campaign_recipients
            WHERE campaign_id = ? AND tenant_id = ? AND status = "pendente"
        ');
        $stmt->execute([$campanhaId, $this->tenantId]);
        $recipients = $stmt->fetchAll(PDO::FETCH_ASSOC);

        foreach ($recipients as $recipient) {
            try {
                // Personalizar conteúdo
                $conteudoHtml = $this->personalizarConteudo($campanha['conteudo_html'], $recipient);
                $assunto = $this->personalizarConteudo($campanha['assunto'], $recipient);

                // Adicionar tracking de abertura e cliques
                if ($this->config['track_opens']) {
                    $conteudoHtml = $this->adicionarTrackingAbertura($conteudoHtml, $recipient['id']);
                }

                if ($this->config['track_clicks']) {
                    $conteudoHtml = $this->adicionarTrackingCliques($conteudoHtml, $recipient['id']);
                }

                // Adicionar link de descadastro
                $conteudoHtml = $this->adicionarLinkDescadastro($conteudoHtml, $recipient);

                // Enviar e-mail
                $enviado = $this->enviarEmail(
                    $recipient['email'],
                    $recipient['nome'] ?? '',
                    $assunto,
                    $conteudoHtml,
                    $campanha['conteudo_texto'] ?? strip_tags($conteudoHtml)
                );

                if ($enviado) {
                    // Atualizar status do destinatário (assumir que enviado = entregue por enquanto)
                    $stmt = $this->pdo->prepare('
                        UPDATE email_campaign_recipients
                        SET status = "entregue", data_envio = NOW()
                        WHERE id = ?
                    ');
                    $stmt->execute([$recipient['id']]);
                    $enviados++;
                } else {
                    // Marcar como erro
                    $stmt = $this->pdo->prepare('
                        UPDATE email_campaign_recipients
                        SET status = "erro"
                        WHERE id = ?
                    ');
                    $stmt->execute([$recipient['id']]);
                    $erros++;
                }

                // Rate limiting: enviar no máximo 10 e-mails por segundo
                if (($enviados + $erros) % 10 === 0) {
                    usleep(1000000); // 1 segundo
                }
            } catch (Exception $e) {
                $erros++;
                $this->registrarLog('email', 'erro', "Erro ao enviar e-mail para {$recipient['email']}: " . $e->getMessage(), [
                    'campanha_id' => $campanhaId,
                    'recipient_id' => $recipient['id']
                ]);
            }
        }

        return [
            'total' => count($recipients),
            'enviados' => $enviados,
            'erros' => $erros,
            'rejeitados' => $rejeitados
        ];
    }

    /**
     * Personaliza conteúdo com variáveis
     */
    private function personalizarConteudo(string $conteudo, array $recipient): string {
        $variaveis = [
            '{{nome}}' => $recipient['nome'] ?? 'Cliente',
            '{{email}}' => $recipient['email'],
            '{{empresa}}' => $this->config['smtp_from_name']
        ];

        // Buscar dados adicionais do cliente/lead
        if ($recipient['cliente_id']) {
            $stmt = $this->pdo->prepare('SELECT * FROM clients WHERE id = ?');
            $stmt->execute([$recipient['cliente_id']]);
            $cliente = $stmt->fetch(PDO::FETCH_ASSOC);
            if ($cliente) {
                $variaveis['{{nome_cliente}}'] = $cliente['nome'] ?? '';
                $variaveis['{{telefone}}'] = $cliente['telefone_celular'] ?? '';
            }
        }

        if ($recipient['lead_id']) {
            $stmt = $this->pdo->prepare('SELECT * FROM leads WHERE id = ?');
            $stmt->execute([$recipient['lead_id']]);
            $lead = $stmt->fetch(PDO::FETCH_ASSOC);
            if ($lead) {
                $variaveis['{{nome_lead}}'] = $lead['nome'] ?? '';
                $variaveis['{{telefone}}'] = $lead['telefone'] ?? '';
            }
        }

        return str_replace(array_keys($variaveis), array_values($variaveis), $conteudo);
    }

    /**
     * Adiciona tracking de abertura (pixel transparente)
     */
    private function adicionarTrackingAbertura(string $html, int $recipientId): string {
        $token = base64_encode($this->tenantId . '_' . $recipientId . '_' . time());
        $baseUrl = getenv('BASE_URL') ?: 'http://localhost';
        $trackingUrl = $baseUrl . '/api/crm/track/open/' . $token;
        $pixel = '<img src="' . $trackingUrl . '" width="1" height="1" style="display:none" alt="" />';
        
        // Adicionar antes do </body> ou no final do HTML
        if (strpos($html, '</body>') !== false) {
            $html = str_replace('</body>', $pixel . '</body>', $html);
        } else {
            $html .= $pixel;
        }

        return $html;
    }

    /**
     * Adiciona tracking de cliques (links rastreados)
     */
    private function adicionarTrackingCliques(string $html, int $recipientId): string {
        // Encontrar todos os links
        preg_match_all('/<a\s+href=["\']([^"\']+)["\'][^>]*>/i', $html, $matches);

        $baseUrl = getenv('BASE_URL') ?: 'http://localhost';

        foreach ($matches[1] as $url) {
            // Ignorar links de tracking e descadastro
            if (strpos($url, '/track/') !== false || strpos($url, '/unsubscribe') !== false) {
                continue;
            }

            // Criar link de tracking
            $token = base64_encode($this->tenantId . '_' . $recipientId . '_' . base64_encode($url));
            $trackingUrl = $baseUrl . '/api/crm/track/click/' . $token;
            $html = str_replace('href="' . $url . '"', 'href="' . $trackingUrl . '"', $html);
        }

        return $html;
    }

    /**
     * Adiciona link de descadastro
     */
    private function adicionarLinkDescadastro(string $html, array $recipient): string {
        $unsubscribeUrl = $this->config['unsubscribe_url'] . '?token=' . base64_encode($recipient['id'] . '_' . $this->tenantId);
        $link = '<p style="font-size:12px;color:#999;"><a href="' . $unsubscribeUrl . '">Descadastrar-se</a></p>';
        
        // Adicionar antes do </body> ou no final do HTML
        if (strpos($html, '</body>') !== false) {
            $html = str_replace('</body>', $link . '</body>', $html);
        } else {
            $html .= $link;
        }

        return $html;
    }

    /**
     * Envia e-mail individual
     */
    private function enviarEmail(string $to, string $toName, string $subject, string $htmlBody, string $textBody): bool {
        try {
            // Usar Mailer existente ou implementar envio via SMTP
            $headers = [
                'From' => $this->config['smtp_from_name'] . ' <' . $this->config['smtp_from'] . '>',
                'Reply-To' => $this->config['smtp_from'],
                'MIME-Version' => '1.0',
                'Content-Type' => 'text/html; charset=UTF-8',
                'X-Mailer' => 'GestorEasy CRM'
            ];

            // Se tiver configuração SMTP, usar PHPMailer ou implementação própria
            if (!empty($this->config['smtp_host']) && $this->config['smtp_host'] !== 'localhost') {
                return $this->enviarViaSMTP($to, $toName, $subject, $htmlBody, $textBody, $headers);
            }

            // Fallback: usar Mailer básico
            return Mailer::send($to, $subject, $textBody, $headers);
        } catch (Exception $e) {
            $this->registrarLog('email', 'erro', "Erro ao enviar e-mail: " . $e->getMessage(), [
                'to' => $to,
                'subject' => $subject
            ]);
            return false;
        }
    }

    /**
     * Envia e-mail via SMTP
     */
    private function enviarViaSMTP(string $to, string $toName, string $subject, string $htmlBody, string $textBody, array $headers): bool {
        // Implementação básica de SMTP usando cURL ou fsockopen
        // Em produção, usar PHPMailer ou similar

        $smtpHost = $this->config['smtp_host'];
        $smtpPort = $this->config['smtp_port'];
        $smtpUser = $this->config['smtp_user'];
        $smtpPass = $this->config['smtp_pass'];
        $smtpFrom = $this->config['smtp_from'];
        $smtpFromName = $this->config['smtp_from_name'];

        // Montar mensagem MIME
        $boundary = uniqid('boundary_');
        $message = "From: {$smtpFromName} <{$smtpFrom}>\r\n";
        $message .= "To: {$toName} <{$to}>\r\n";
        $message .= "Subject: {$subject}\r\n";
        $message .= "MIME-Version: 1.0\r\n";
        $message .= "Content-Type: multipart/alternative; boundary=\"{$boundary}\"\r\n\r\n";
        $message .= "--{$boundary}\r\n";
        $message .= "Content-Type: text/plain; charset=UTF-8\r\n";
        $message .= "Content-Transfer-Encoding: 8bit\r\n\r\n";
        $message .= $textBody . "\r\n\r\n";
        $message .= "--{$boundary}\r\n";
        $message .= "Content-Type: text/html; charset=UTF-8\r\n";
        $message .= "Content-Transfer-Encoding: 8bit\r\n\r\n";
        $message .= $htmlBody . "\r\n\r\n";
        $message .= "--{$boundary}--\r\n";

        // Tentar enviar via socket SMTP
        try {
            $socket = @fsockopen($smtpHost, $smtpPort, $errno, $errstr, 10);
            if (!$socket) {
                throw new Exception("Não foi possível conectar ao servidor SMTP: {$errstr}");
            }

            $this->smtpRead($socket); // Ler resposta inicial

            // EHLO/HELO
            fwrite($socket, "EHLO " . $smtpHost . "\r\n");
            $this->smtpRead($socket);

            // STARTTLS se necessário
            if ($smtpPort === 587) {
                fwrite($socket, "STARTTLS\r\n");
                $this->smtpRead($socket);
                stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
                fwrite($socket, "EHLO " . $smtpHost . "\r\n");
                $this->smtpRead($socket);
            }

            // Autenticação
            if (!empty($smtpUser) && !empty($smtpPass)) {
                fwrite($socket, "AUTH LOGIN\r\n");
                $this->smtpRead($socket);
                fwrite($socket, base64_encode($smtpUser) . "\r\n");
                $this->smtpRead($socket);
                fwrite($socket, base64_encode($smtpPass) . "\r\n");
                $response = $this->smtpRead($socket);
                if (strpos($response, '235') === false) {
                    throw new Exception('Falha na autenticação SMTP');
                }
            }

            // MAIL FROM
            fwrite($socket, "MAIL FROM: <{$smtpFrom}>\r\n");
            $this->smtpRead($socket);

            // RCPT TO
            fwrite($socket, "RCPT TO: <{$to}>\r\n");
            $this->smtpRead($socket);

            // DATA
            fwrite($socket, "DATA\r\n");
            $this->smtpRead($socket);
            fwrite($socket, $message . "\r\n.\r\n");
            $response = $this->smtpRead($socket);
            if (strpos($response, '250') === false) {
                throw new Exception('Falha ao enviar mensagem');
            }

            // QUIT
            fwrite($socket, "QUIT\r\n");
            fclose($socket);

            return true;
        } catch (Exception $e) {
            if (isset($socket)) {
                fclose($socket);
            }
            throw $e;
        }
    }

    /**
     * Lê resposta do servidor SMTP
     */
    private function smtpRead($socket): string {
        $response = '';
        while ($line = fgets($socket, 515)) {
            $response .= $line;
            if (substr($line, 3, 1) === ' ') {
                break;
            }
        }
        return $response;
    }

    /**
     * Atualiza estatísticas da campanha
     */
    private function atualizarEstatisticasCampanha(int $campanhaId, array $resultado): void {
        // Buscar estatísticas reais do banco
        $stmt = $this->pdo->prepare('
            SELECT 
                COUNT(*) as total,
                SUM(CASE WHEN status IN ("enviado", "entregue", "aberto", "clicado") THEN 1 ELSE 0 END) as enviados,
                SUM(CASE WHEN status IN ("entregue", "aberto", "clicado") THEN 1 ELSE 0 END) as entregues,
                SUM(CASE WHEN status = "rejeitado" THEN 1 ELSE 0 END) as rejeitados
            FROM email_campaign_recipients
            WHERE campaign_id = ? AND tenant_id = ?
        ');
        $stmt->execute([$campanhaId, $this->tenantId]);
        $stats = $stmt->fetch(PDO::FETCH_ASSOC);

        $stmt = $this->pdo->prepare('
            UPDATE email_campaigns
            SET 
                total_destinatarios = ?,
                total_enviados = ?,
                total_entregues = ?,
                total_rejeitados = ?
            WHERE id = ? AND tenant_id = ?
        ');
        $stmt->execute([
            (int)($stats['total'] ?? 0),
            (int)($stats['enviados'] ?? 0),
            (int)($stats['entregues'] ?? 0),
            (int)($stats['rejeitados'] ?? 0),
            $campanhaId,
            $this->tenantId
        ]);
    }

    /**
     * Registra abertura de e-mail
     */
    public function registrarAbertura(string $token): void {
        try {
            $data = base64_decode($token);
            $parts = explode('_', $data);
            
            if (count($parts) < 2) {
                return;
            }

            $tenantId = $parts[0];
            $recipientId = (int)$parts[1];

            // Usar PDO do tenant correto
            $pdo = ConnectionManager::forTenant($tenantId);

            // Verificar se já foi aberto (evitar contagem duplicada)
            $checkStmt = $pdo->prepare('SELECT data_abertura FROM email_campaign_recipients WHERE id = ? AND tenant_id = ?');
            $checkStmt->execute([$recipientId, $tenantId]);
            $existing = $checkStmt->fetch(PDO::FETCH_ASSOC);

            if ($existing && $existing['data_abertura']) {
                // Já foi aberto antes, não contar novamente
                return;
            }

            $stmt = $pdo->prepare('
                UPDATE email_campaign_recipients
                SET status = "aberto", data_abertura = NOW(),
                    ip_abertura = ?, user_agent_abertura = ?
                WHERE id = ? AND tenant_id = ?
            ');
            $stmt->execute([
                $_SERVER['REMOTE_ADDR'] ?? null,
                $_SERVER['HTTP_USER_AGENT'] ?? null,
                $recipientId,
                $tenantId
            ]);

            // Atualizar estatísticas da campanha
            $stmt = $pdo->prepare('
                SELECT campaign_id FROM email_campaign_recipients WHERE id = ?
            ');
            $stmt->execute([$recipientId]);
            $recipient = $stmt->fetch(PDO::FETCH_ASSOC);

            if ($recipient) {
                $stmt = $pdo->prepare('
                    UPDATE email_campaigns
                    SET total_abertos = total_abertos + 1
                    WHERE id = ? AND tenant_id = ?
                ');
                $stmt->execute([$recipient['campaign_id'], $tenantId]);
            }
        } catch (Exception $e) {
            // Ignorar erros de tracking
        }
    }

    /**
     * Registra clique em link
     */
    public function registrarClique(string $token): ?string {
        try {
            $data = base64_decode($token);
            $parts = explode('_', $data);
            
            if (count($parts) < 3) {
                return null;
            }

            $tenantId = $parts[0];
            $recipientId = (int)$parts[1];
            $urlOriginal = base64_decode($parts[2] ?? '');

            if (empty($urlOriginal)) {
                return null;
            }

            // Usar PDO do tenant correto
            $pdo = ConnectionManager::forTenant($tenantId);

            // Verificar se já foi clicado (evitar contagem duplicada na mesma sessão)
            $checkStmt = $pdo->prepare('SELECT data_clique FROM email_campaign_recipients WHERE id = ? AND tenant_id = ?');
            $checkStmt->execute([$recipientId, $tenantId]);
            $existing = $checkStmt->fetch(PDO::FETCH_ASSOC);

            // Atualizar status para clicado
            $stmt = $pdo->prepare('
                UPDATE email_campaign_recipients
                SET status = "clicado", data_clique = COALESCE(data_clique, NOW())
                WHERE id = ? AND tenant_id = ?
            ');
            $stmt->execute([$recipientId, $tenantId]);

            // Atualizar estatísticas da campanha (somente se for o primeiro clique)
            if (!$existing || !$existing['data_clique']) {
                $stmt = $pdo->prepare('
                    SELECT campaign_id FROM email_campaign_recipients WHERE id = ?
                ');
                $stmt->execute([$recipientId]);
                $recipient = $stmt->fetch(PDO::FETCH_ASSOC);

                if ($recipient) {
                    $stmt = $pdo->prepare('
                        UPDATE email_campaigns
                        SET total_cliques = total_cliques + 1
                        WHERE id = ? AND tenant_id = ?
                    ');
                    $stmt->execute([$recipient['campaign_id'], $tenantId]);
                }
            }

            return $urlOriginal;
        } catch (Exception $e) {
            return null;
        }
    }

    /**
     * Registra log
     */
    private function registrarLog(string $tipo, string $status, string $mensagem, array $detalhes = []): void {
        // Log pode ser salvo em uma tabela específica ou arquivo
        error_log("[CRM Email] {$status}: {$mensagem}");
    }
}

