<?php
declare(strict_types=1);

class WhatsAppService
{
    public static function getSettings(): array
    {
        $row = db()->query('SELECT * FROM whatsapp_accounts ORDER BY id ASC LIMIT 1')->fetch();

        return $row ?: [
            'provider' => 'meta_cloud',
            'phone_number_id' => '',
            'access_token' => '',
            'business_account_id' => '',
            'default_country_code' => '55',
            'is_enabled' => 0,
            'verify_token' => '',
            'default_pipeline_id' => null,
            'default_stage_id' => null,
            'webhook_secret' => '',
        ];
    }

    public static function saveSettings(array $data): void
    {
        $existing = db()->query('SELECT id FROM whatsapp_accounts ORDER BY id ASC LIMIT 1')->fetchColumn();

        $payload = [
            'provider' => trim((string)($data['provider'] ?? 'meta_cloud')),
            'phone_number_id' => trim((string)($data['phone_number_id'] ?? '')),
            'access_token' => trim((string)($data['access_token'] ?? '')),
            'business_account_id' => trim((string)($data['business_account_id'] ?? '')),
            'default_country_code' => trim((string)($data['default_country_code'] ?? '55')),
            'is_enabled' => !empty($data['is_enabled']) ? 1 : 0,
            'verify_token' => trim((string)($data['verify_token'] ?? '')),
            'default_pipeline_id' => !empty($data['default_pipeline_id']) ? (int)$data['default_pipeline_id'] : null,
            'default_stage_id' => !empty($data['default_stage_id']) ? (int)$data['default_stage_id'] : null,
            'webhook_secret' => trim((string)($data['webhook_secret'] ?? '')),
        ];

        if ($existing) {
            $stmt = db()->prepare('
                UPDATE whatsapp_accounts
                SET
                    provider=:provider,
                    phone_number_id=:phone_number_id,
                    access_token=:access_token,
                    business_account_id=:business_account_id,
                    default_country_code=:default_country_code,
                    is_enabled=:is_enabled,
                    verify_token=:verify_token,
                    default_pipeline_id=:default_pipeline_id,
                    default_stage_id=:default_stage_id,
                    webhook_secret=:webhook_secret,
                    updated_at=NOW()
                WHERE id=:id
            ');
            $payload['id'] = (int)$existing;
            $stmt->execute($payload);
        } else {
            $stmt = db()->prepare('
                INSERT INTO whatsapp_accounts
                (provider, phone_number_id, access_token, business_account_id, default_country_code, is_enabled,
                 verify_token, default_pipeline_id, default_stage_id, webhook_secret, created_at, updated_at)
                VALUES
                (:provider, :phone_number_id, :access_token, :business_account_id, :default_country_code, :is_enabled,
                 :verify_token, :default_pipeline_id, :default_stage_id, :webhook_secret, NOW(), NOW())
            ');
            $stmt->execute($payload);
        }
    }

    public static function formatPhone(string $phone): string
    {
        return preg_replace('/\D+/', '', $phone);
    }

    /**
     * Envia texto ou anexo para contato via Cloud API e registra em whatsapp_messages.
     */
    public static function sendToContact(int $contactId, string $message, ?int $templateId = null, ?array $attachment = null): array
    {
        $contactStmt = db()->prepare('SELECT * FROM contacts WHERE id = :id LIMIT 1');
        $contactStmt->execute(['id' => $contactId]);
        $contact = $contactStmt->fetch();

        if (!$contact) {
            return ['success' => false, 'message' => 'Contato não encontrado.'];
        }

        if (empty($contact['phone'])) {
            return ['success' => false, 'message' => 'Contato sem telefone cadastrado.'];
        }

        $attachmentData = self::normalizeAttachment($attachment);

        if ($message === '' && $attachmentData === null) {
            return ['success' => false, 'message' => 'Envie uma mensagem ou um anexo.'];
        }

        $settings = self::getSettings();

        $providerStatus  = 'simulation';
        $externalId      = null;
        $externalMediaId = null;

        $historyBody = self::buildHistoryBody($message, $attachmentData);

        if ((int)($settings['is_enabled'] ?? 0) === 1 && !empty($settings['access_token']) && !empty($settings['phone_number_id'])) {
            $providerStatus = 'queued';

            $phone = self::formatPhone((string)$contact['phone']);

            if ($attachmentData !== null) {
                $uploadResult = self::uploadMedia($settings, $attachmentData);

                if (!$uploadResult['success']) {
                    $providerStatus = 'failed';
                    write_system_log(
                        'whatsapp',
                        'Falha upload mídia contato ' . $contactId .
                        ' HTTP=' . (string)$uploadResult['http'] .
                        ' RESP=' . (string)$uploadResult['response']
                    );
                } else {
                    $externalMediaId = (string)$uploadResult['media_id'];

                    $payload = self::buildMediaMessagePayload($phone, $attachmentData, $message, $externalMediaId);
                    $sendResult = self::sendMessagePayload($settings, $payload);

                    if (!$sendResult['success']) {
                        $providerStatus = 'failed';
                        write_system_log(
                            'whatsapp',
                            'Falha envio mídia contato ' . $contactId .
                            ' HTTP=' . (string)$sendResult['http'] .
                            ' RESP=' . (string)$sendResult['response']
                        );
                    } else {
                        $externalId = $sendResult['message_id'];
                        $providerStatus = 'sent';
                    }
                }
            } else {
                $payload = self::buildTextMessagePayload($phone, $message);
                $sendResult = self::sendMessagePayload($settings, $payload);

                if (!$sendResult['success']) {
                    $providerStatus = 'failed';
                    write_system_log(
                        'whatsapp',
                        'Falha envio texto contato ' . $contactId .
                        ' HTTP=' . (string)$sendResult['http'] .
                        ' RESP=' . (string)$sendResult['response']
                    );
                } else {
                    $externalId = $sendResult['message_id'];
                    $providerStatus = 'sent';
                }
            }
        }

        $stmt = db()->prepare('
            INSERT INTO whatsapp_messages
            (
                contact_id,
                user_id,
                template_id,
                direction,
                provider_status,
                external_message_id,
                external_media_id,
                message_body,
                media_type,
                media_mime_type,
                media_file_name,
                media_file_size,
                created_at
            )
            VALUES
            (
                :contact_id,
                :user_id,
                :template_id,
                :direction,
                :provider_status,
                :external_message_id,
                :external_media_id,
                :message_body,
                :media_type,
                :media_mime_type,
                :media_file_name,
                :media_file_size,
                NOW()
            )
        ');

        $stmt->execute([
            'contact_id'          => $contactId,
            'user_id'             => (int)(current_user()['id'] ?? 0) ?: null,
            'template_id'         => $templateId,
            'direction'           => 'outbound',
            'provider_status'     => $providerStatus,
            'external_message_id' => $externalId,
            'external_media_id'   => $externalMediaId,
            'message_body'        => $historyBody,
            'media_type'          => $attachmentData['media_type'] ?? null,
            'media_mime_type'     => $attachmentData['mime'] ?? null,
            'media_file_name'     => $attachmentData['name'] ?? null,
            'media_file_size'     => $attachmentData['size'] ?? null,
        ]);

        log_activity(
            $contactId,
            'whatsapp',
            'Mensagem WhatsApp enviada: ' . $historyBody,
            (int)(current_user()['id'] ?? 0) ?: null
        );

        if ($providerStatus === 'failed') {
            return ['success' => false, 'message' => 'Falha ao enviar a mensagem/anexo para o provider.'];
        }

        if ($providerStatus === 'simulation') {
            return ['success' => true, 'message' => 'Mensagem/anexo registrado em modo simulação.'];
        }

        return ['success' => true, 'message' => 'Mensagem/anexo enviado ao provider.'];
    }

    public static function registerInboundMessage(int $contactId, string $waId, ?string $messageId, string $body, array $rawPayload = []): void
    {
        $stmt = db()->prepare('
            INSERT INTO whatsapp_messages
            (contact_id, user_id, template_id, direction, provider_status, external_message_id, message_body, created_at)
            VALUES
            (:contact_id, NULL, NULL, :direction, :provider_status, :external_message_id, :message_body, NOW())
        ');
        $stmt->execute([
            'contact_id' => $contactId,
            'direction' => 'inbound',
            'provider_status' => 'received',
            'external_message_id' => $messageId,
            'message_body' => $body,
        ]);

        log_activity($contactId, 'whatsapp', 'Mensagem WhatsApp recebida: ' . $body, null);
    }

    public static function fetchMediaForMessage(int $messageId): array
    {
        $stmt = db()->prepare('
            SELECT id, media_type, media_file_name, media_mime_type, external_media_id
            FROM whatsapp_messages
            WHERE id = :id
            LIMIT 1
        ');
        $stmt->execute(['id' => $messageId]);
        $row = $stmt->fetch();

        if (!$row) {
            return ['success' => false, 'message' => 'Mensagem não encontrada.'];
        }

        $externalMediaId = trim((string)($row['external_media_id'] ?? ''));
        if ($externalMediaId === '') {
            return ['success' => false, 'message' => 'Esta mensagem não possui mídia para download.'];
        }

        $settings = self::getSettings();
        if (empty($settings['access_token'])) {
            return ['success' => false, 'message' => 'Token do WhatsApp não configurado.'];
        }

        $metaInfo = self::fetchMediaMeta($externalMediaId, (string)$settings['access_token']);
        if (!$metaInfo['success']) {
            return ['success' => false, 'message' => $metaInfo['message']];
        }

        $binary = self::downloadMediaBinary((string)$metaInfo['url'], (string)$settings['access_token']);
        if (!$binary['success']) {
            return ['success' => false, 'message' => $binary['message']];
        }

        $mime = (string)($metaInfo['mime_type'] ?? $row['media_mime_type'] ?? 'application/octet-stream');
        $mediaType = (string)($row['media_type'] ?? '');
        $fileName = trim((string)($row['media_file_name'] ?? ''));

        if ($fileName === '') {
            $fileName = self::buildDownloadedMediaFileName($mediaType, $mime, $messageId);
        }

        return [
            'success'   => true,
            'content'   => $binary['content'],
            'mime_type' => $mime !== '' ? $mime : 'application/octet-stream',
            'file_name' => $fileName,
        ];
    }

    private static function buildTextMessagePayload(string $phone, string $message): array
    {
        return [
            'messaging_product' => 'whatsapp',
            'to' => $phone,
            'type' => 'text',
            'text' => [
                'preview_url' => false,
                'body' => $message,
            ],
        ];
    }

    private static function buildMediaMessagePayload(string $phone, array $attachment, string $message, string $mediaId): array
    {
        $payload = [
            'messaging_product' => 'whatsapp',
            'to' => $phone,
            'type' => $attachment['media_type'],
        ];

        if ($attachment['media_type'] === 'image') {
            $payload['image'] = ['id' => $mediaId];
            if ($message !== '') {
                $payload['image']['caption'] = $message;
            }
            return $payload;
        }

        if ($attachment['media_type'] === 'video') {
            $payload['video'] = ['id' => $mediaId];
            if ($message !== '') {
                $payload['video']['caption'] = $message;
            }
            return $payload;
        }

        if ($attachment['media_type'] === 'audio') {
            $payload['audio'] = ['id' => $mediaId];
            return $payload;
        }

        $payload['type'] = 'document';
        $payload['document'] = [
            'id' => $mediaId,
            'filename' => (string)$attachment['name'],
        ];
        if ($message !== '') {
            $payload['document']['caption'] = $message;
        }

        return $payload;
    }

    private static function uploadMedia(array $settings, array $attachment): array
    {
        $url = 'https://graph.facebook.com/v20.0/' . rawurlencode((string)$settings['phone_number_id']) . '/media';

        $postFields = [
            'messaging_product' => 'whatsapp',
            'file' => new CURLFile(
                (string)$attachment['tmp_name'],
                (string)$attachment['mime'],
                (string)$attachment['name']
            ),
        ];

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . $settings['access_token'],
            ],
            CURLOPT_POSTFIELDS => $postFields,
            CURLOPT_TIMEOUT => 60,
        ]);

        $response = curl_exec($ch);
        $errno = curl_errno($ch);
        $http = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        $decoded = json_decode((string)$response, true);

        if ($errno || $http >= 300 || empty($decoded['id'])) {
            return [
                'success' => false,
                'media_id' => null,
                'http' => $http,
                'response' => (string)$response,
            ];
        }

        return [
            'success' => true,
            'media_id' => (string)$decoded['id'],
            'http' => $http,
            'response' => (string)$response,
        ];
    }

    private static function sendMessagePayload(array $settings, array $payload): array
    {
        $url = 'https://graph.facebook.com/v20.0/' . rawurlencode((string)$settings['phone_number_id']) . '/messages';

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . $settings['access_token'],
                'Content-Type: application/json',
            ],
            CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
            CURLOPT_TIMEOUT => 30,
        ]);

        $response = curl_exec($ch);
        $errno = curl_errno($ch);
        $http = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        $decoded = json_decode((string)$response, true);
        $messageId = $decoded['messages'][0]['id'] ?? null;

        if ($errno || $http >= 300 || $messageId === null) {
            return [
                'success' => false,
                'message_id' => null,
                'http' => $http,
                'response' => (string)$response,
            ];
        }

        return [
            'success' => true,
            'message_id' => (string)$messageId,
            'http' => $http,
            'response' => (string)$response,
        ];
    }

    private static function fetchMediaMeta(string $mediaId, string $accessToken): array
    {
        $url = 'https://graph.facebook.com/v20.0/' . rawurlencode($mediaId);

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . $accessToken,
            ],
            CURLOPT_TIMEOUT => 30,
        ]);

        $response = curl_exec($ch);
        $errno = curl_errno($ch);
        $http = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        $decoded = json_decode((string)$response, true);

        if ($errno || $http >= 300 || empty($decoded['url'])) {
            write_system_log(
                'whatsapp',
                'Falha obter meta da mídia media_id=' . $mediaId .
                ' HTTP=' . $http .
                ' RESP=' . (string)$response
            );

            return [
                'success' => false,
                'message' => 'Não foi possível localizar a mídia na Meta.',
            ];
        }

        return [
            'success'   => true,
            'url'       => (string)$decoded['url'],
            'mime_type' => (string)($decoded['mime_type'] ?? ''),
        ];
    }

    private static function downloadMediaBinary(string $url, string $accessToken): array
    {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . $accessToken,
            ],
            CURLOPT_TIMEOUT => 60,
        ]);

        $response = curl_exec($ch);
        $errno = curl_errno($ch);
        $http = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($errno || $http >= 300 || $response === false) {
            write_system_log(
                'whatsapp',
                'Falha download binário mídia HTTP=' . $http . ' URL=' . $url
            );

            return [
                'success' => false,
                'message' => 'Não foi possível baixar a mídia.',
            ];
        }

        return [
            'success' => true,
            'content' => (string)$response,
        ];
    }

    private static function normalizeAttachment(?array $attachment): ?array
    {
        if ($attachment === null) {
            return null;
        }

        $tmpName = (string)($attachment['tmp_name'] ?? '');
        $error = (int)($attachment['error'] ?? UPLOAD_ERR_NO_FILE);

        if ($error !== UPLOAD_ERR_OK || $tmpName === '' || !is_file($tmpName)) {
            return null;
        }

        $name = self::sanitizeFileName((string)($attachment['name'] ?? 'arquivo'));
        $size = (int)($attachment['size'] ?? 0);
        $mime = self::detectMimeType($tmpName, (string)($attachment['type'] ?? 'application/octet-stream'));
        $mediaType = self::detectMediaType($mime, $name);

        return [
            'tmp_name' => $tmpName,
            'name' => $name,
            'size' => $size,
            'mime' => $mime,
            'media_type' => $mediaType,
        ];
    }

    private static function sanitizeFileName(string $name): string
    {
        $name = trim(basename($name));
        $name = preg_replace('/[^\pL\pN\.\-_]+/u', '_', $name) ?? 'arquivo';

        if ($name === '' || $name === '.' || $name === '..') {
            return 'arquivo';
        }

        return $name;
    }

    private static function detectMimeType(string $tmpName, string $fallback): string
    {
        $mime = '';

        if (function_exists('finfo_open')) {
            $finfo = finfo_open(FILEINFO_MIME_TYPE);
            if ($finfo) {
                $detected = finfo_file($finfo, $tmpName);
                finfo_close($finfo);
                if (is_string($detected) && $detected !== '') {
                    $mime = $detected;
                }
            }
        }

        if ($mime === '') {
            $mime = trim($fallback) !== '' ? trim($fallback) : 'application/octet-stream';
        }

        return $mime;
    }

    private static function detectMediaType(string $mime, string $name): string
    {
        $ext = strtolower((string)pathinfo($name, PATHINFO_EXTENSION));

        if (str_starts_with($mime, 'image/')) {
            return 'image';
        }

        if (str_starts_with($mime, 'video/')) {
            return 'video';
        }

        if (str_starts_with($mime, 'audio/')) {
            return 'audio';
        }

        if (in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp'], true)) {
            return 'image';
        }

        if (in_array($ext, ['mp4', 'mov', 'avi', 'mkv', '3gp', 'webm'], true)) {
            return 'video';
        }

        if (in_array($ext, ['mp3', 'ogg', 'wav', 'aac', 'm4a', 'opus'], true)) {
            return 'audio';
        }

        return 'document';
    }

    private static function buildHistoryBody(string $message, ?array $attachment): string
    {
        $message = trim($message);

        if ($attachment === null) {
            return $message;
        }

        if ($message !== '') {
            return $message;
        }

        return self::mediaPlaceholderLabel((string)$attachment['media_type'], (string)$attachment['name']);
    }

    private static function mediaPlaceholderLabel(string $mediaType, string $fileName): string
    {
        return match ($mediaType) {
            'image' => '[Imagem enviada: ' . $fileName . ']',
            'video' => '[Vídeo enviado: ' . $fileName . ']',
            'audio' => '[Áudio enviado: ' . $fileName . ']',
            default => '[Documento enviado: ' . $fileName . ']',
        };
    }

    private static function buildDownloadedMediaFileName(string $mediaType, string $mime, int $messageId): string
    {
        $ext = self::extensionFromMime($mime);

        $base = match ($mediaType) {
            'image' => 'imagem_whatsapp_' . $messageId,
            'video' => 'video_whatsapp_' . $messageId,
            'audio' => 'audio_whatsapp_' . $messageId,
            default => 'arquivo_whatsapp_' . $messageId,
        };

        return $ext !== '' ? ($base . '.' . $ext) : $base;
    }

    private static function extensionFromMime(string $mime): string
    {
        return match (strtolower(trim($mime))) {
            'image/jpeg' => 'jpg',
            'image/png' => 'png',
            'image/gif' => 'gif',
            'image/webp' => 'webp',
            'video/mp4' => 'mp4',
            'video/3gpp' => '3gp',
            'audio/mpeg' => 'mp3',
            'audio/ogg' => 'ogg',
            'audio/aac' => 'aac',
            'audio/mp4' => 'm4a',
            'application/pdf' => 'pdf',
            'application/msword' => 'doc',
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
            'application/vnd.ms-excel' => 'xls',
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
            'text/plain' => 'txt',
            'text/csv' => 'csv',
            default => '',
        };
    }
}