<?php

declare(strict_types=1);

namespace App\Service\Whatsapp;

use App\Entity\Ecole;
use Psr\Log\LoggerInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;

final class GreenApiClient
{
    public function __construct(
        private readonly HttpClientInterface $httpClient,
        private readonly LoggerInterface $logger,
        private readonly PhoneNumberNormalizer $phoneNumberNormalizer,
        private readonly string $globalInstanceId = '',
        private readonly string $globalApiToken = '',
        private readonly string $baseUrl = 'https://api.green-api.com',
        private readonly string $mediaUrl = 'https://media.green-api.com',
        private readonly bool $verifySsl = true,
    ) {
    }

    /** @return array{success: bool, message: string, data?: mixed, chatId?: string} */
    public function sendTextMessage(string $phone, string $message, ?Ecole $ecole = null): array
    {
        return $this->sendToPhone($phone, static fn (string $chatId) => [
            'endpoint' => 'sendMessage',
            'payload' => ['chatId' => $chatId, 'message' => $message],
        ], $ecole);
    }

    /** @return array{success: bool, message: string, data?: mixed, chatId?: string} */
    public function sendFileByUrl(string $phone, string $fileUrl, string $fileName, string $caption = '', ?Ecole $ecole = null): array
    {
        return $this->sendToPhone($phone, static fn (string $chatId) => [
            'endpoint' => 'sendFileByUrl',
            'payload' => [
                'chatId' => $chatId,
                'urlFile' => $fileUrl,
                'fileName' => $fileName,
                'caption' => $caption,
            ],
        ], $ecole);
    }

    /** @return array{success: bool, message: string, data?: mixed, chatId?: string} */
    public function sendFileByUpload(string $phone, string $absolutePath, string $fileName, string $caption = '', ?Ecole $ecole = null): array
    {
        if (!is_readable($absolutePath)) {
            return ['success' => false, 'message' => 'Fichier introuvable pour l’envoi WhatsApp.'];
        }

        $cfg = $this->configuration($ecole);
        if (!$cfg) {
            return ['success' => false, 'message' => 'Green API n’est pas configuré.'];
        }

        try {
            $data = $this->uploadLocalFile($absolutePath, $fileName, $cfg);
            $fileUrl = $data['urlFile'] ?? $data['data']['urlFile'] ?? null;

            if (!$fileUrl) {
                return ['success' => false, 'message' => 'Green API n’a pas retourné d’URL de fichier.', 'data' => $data];
            }

            return $this->sendFileByUrl($phone, (string) $fileUrl, $fileName, $caption, $ecole);
        } catch (\Throwable $exception) {
            $this->logger->error('green_api.upload.error', ['exception' => $exception]);

            return ['success' => false, 'message' => $exception->getMessage()];
        }
    }

    /**
     * @param array{instanceId: string, apiToken: string, scope: string} $cfg
     *
     * @return array<string, mixed>
     */
    private function uploadLocalFile(string $absolutePath, string $fileName, array $cfg): array
    {
        $mime = mime_content_type($absolutePath) ?: 'application/pdf';
        $url = sprintf('%s/waInstance%s/uploadFile/%s', rtrim($this->mediaUrl ?: $this->baseUrl, '/'), $cfg['instanceId'], $cfg['apiToken']);

        $this->logger->info('green_api.upload.request', [
            'path' => $absolutePath,
            'file_name' => $fileName,
            'mime' => $mime,
            'instance_scope' => $cfg['scope'],
        ]);

        if (!function_exists('curl_init')) {
            $response = $this->httpClient->request('POST', $url, [
                'headers' => [
                    'Content-Type' => $mime,
                    'GA-Filename' => $fileName,
                ],
                'body' => file_get_contents($absolutePath),
                'verify_peer' => $this->verifySsl,
                'verify_host' => $this->verifySsl,
                'timeout' => 30,
            ]);

            $data = $response->toArray(false);
            $this->logger->info('green_api.upload.response', [
                'status_code' => $response->getStatusCode(),
                'data' => $data,
            ]);

            return $data;
        }

        $binary = file_get_contents($absolutePath);
        if ($binary === false) {
            throw new \RuntimeException('Impossible de lire le fichier PDF à envoyer.');
        }

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => $binary,
            CURLOPT_HTTPHEADER => [
                'Content-Type: '.$mime,
                'GA-Filename: '.$fileName,
            ],
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER => true,
            CURLOPT_SSL_VERIFYPEER => $this->verifySsl,
            CURLOPT_SSL_VERIFYHOST => $this->verifySsl ? 2 : 0,
            CURLOPT_CONNECTTIMEOUT => 30,
            CURLOPT_TIMEOUT => 30,
        ]);

        $response = curl_exec($ch);
        $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
        $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);

        if ($error) {
            throw new \RuntimeException($error);
        }

        $body = substr((string) $response, (int) $headerSize);
        $data = json_decode($body, true);
        if (!is_array($data)) {
            $data = ['raw' => $body];
        }

        $this->logger->info('green_api.upload.response', [
            'status_code' => $statusCode,
            'data' => $data,
        ]);

        return $data;
    }

    /** @return array{success: bool, message: string, data?: mixed, chatId?: string} */
    public function addContactToGroup(string $groupId, string $phone, ?Ecole $ecole = null): array
    {
        $groupChatId = $this->toGroupChatId($groupId);
        $chatId = $this->phoneNumberNormalizer->toContactChatId($phone);
        if (!$chatId) {
            $this->logger->warning('green_api.phone.invalid', ['raw_phone' => $phone, 'target' => 'group_participant']);

            return ['success' => false, 'message' => 'Numéro WhatsApp invalide.'];
        }

        $result = $this->request('addGroupParticipant', [
            'groupId' => $groupChatId,
            'participantChatId' => $chatId,
        ], $ecole);

        if ($result['success'] || $this->looksAlreadyInGroup($result['message'] ?? '', $result['data'] ?? null)) {
            return [
                'success' => true,
                'message' => $result['success'] ? 'Participant ajouté au groupe WhatsApp.' : 'Participant déjà présent dans le groupe WhatsApp.',
                'data' => $result['data'] ?? null,
                'chatId' => $chatId,
            ];
        }

        return $result + ['chatId' => $chatId];
    }

    /** @return array{success: bool, message: string, data?: mixed, chatId?: string} */
    public function sendGroupInvite(string $phone, string $inviteLink, string $message, ?Ecole $ecole = null): array
    {
        return $this->sendTextMessage($phone, trim($message)."\n\n".$inviteLink, $ecole);
    }

    public function getGroupInviteLink(string $groupId, ?Ecole $ecole = null): ?string
    {
        $result = $this->request('getGroupData', ['groupId' => $this->toGroupChatId($groupId)], $ecole);
        if (!$result['success']) {
            return null;
        }

        $data = $result['data'] ?? [];
        $groupData = $data['data'] ?? $data;
        $link = $groupData['inviteLink'] ?? $groupData['link'] ?? null;
        if (is_string($link) && trim($link) !== '') {
            return trim($link);
        }

        $inviteCode = $groupData['inviteCode'] ?? null;
        if (is_string($inviteCode) && trim($inviteCode) !== '') {
            return 'https://chat.whatsapp.com/'.trim($inviteCode);
        }

        return null;
    }

    /** @return array{success: bool, message: string, data?: mixed, chatId?: string} */
    private function sendToPhone(string $phone, callable $builder, ?Ecole $ecole): array
    {
        $normalized = $this->phoneNumberNormalizer->normalizeToE164NoPlus($phone);
        $chatId = $normalized ? $normalized.'@c.us' : null;

        $this->logger->info('green_api.phone.normalized', [
            'raw_phone' => $phone,
            'normalized_phone' => $normalized,
            'chat_id' => $chatId,
        ]);

        if (!$chatId) {
            return ['success' => false, 'message' => 'Numéro WhatsApp invalide.'];
        }

        $request = $builder($chatId);
        $result = $this->request($request['endpoint'], $request['payload'], $ecole);

        return $result + ['chatId' => $chatId];
    }

    /** @return array{success: bool, message: string, data?: mixed} */
    private function request(string $endpoint, array $payload, ?Ecole $ecole = null): array
    {
        $cfg = $this->configuration($ecole);
        if (!$cfg) {
            return ['success' => false, 'message' => 'Green API n’est pas configuré.'];
        }

        try {
            $url = sprintf('%s/waInstance%s/%s/%s', rtrim($this->baseUrl, '/'), $cfg['instanceId'], $endpoint, $cfg['apiToken']);
            $this->logger->info('green_api.request', [
                'endpoint' => $endpoint,
                'instance_scope' => $cfg['scope'],
                'payload' => $this->safePayloadForLog($payload),
            ]);

            $response = $this->httpClient->request('POST', $url, [
                'json' => $payload,
                'verify_peer' => $this->verifySsl,
                'verify_host' => $this->verifySsl,
            ]);

            $statusCode = $response->getStatusCode();
            $data = $response->toArray(false);
            $explicitError = $data['error'] ?? $data['data']['error'] ?? null;

            $this->logger->info('green_api.response', [
                'endpoint' => $endpoint,
                'status_code' => $statusCode,
                'data' => $data,
            ]);

            if ($statusCode >= 200 && $statusCode < 300 && !$explicitError && $this->isExpectedSuccessResponse($endpoint, $data)) {
                return ['success' => true, 'message' => 'Message WhatsApp envoyé.', 'data' => $data];
            }

            return [
                'success' => false,
                'message' => is_string($explicitError) ? $explicitError : $this->failureMessage($endpoint, $data),
                'data' => $data,
            ];
        } catch (\Throwable $exception) {
            $this->logger->error('green_api.request.error', [
                'endpoint' => $endpoint,
                'exception' => $exception,
            ]);

            return ['success' => false, 'message' => $exception->getMessage()];
        }
    }

    /** @return null|array{instanceId: string, apiToken: string, scope: string} */
    private function configuration(?Ecole $ecole): ?array
    {
        if (
            $ecole?->isWhatsappInstanceActive()
            && $ecole->getWhatsappInstanceId()
            && $ecole->getWhatsappApiToken()
        ) {
            return [
                'instanceId' => $ecole->getWhatsappInstanceId(),
                'apiToken' => $ecole->getWhatsappApiToken(),
                'scope' => 'ecole',
            ];
        }

        if (trim($this->globalInstanceId) === '' || trim($this->globalApiToken) === '') {
            return null;
        }

        return [
            'instanceId' => trim($this->globalInstanceId),
            'apiToken' => trim($this->globalApiToken),
            'scope' => 'platform',
        ];
    }

    private function toGroupChatId(string $groupId): string
    {
        $groupId = trim($groupId);

        return str_ends_with($groupId, '@g.us') ? $groupId : $groupId.'@g.us';
    }

    private function isExpectedSuccessResponse(string $endpoint, array $data): bool
    {
        if ($endpoint === 'sendMessage' || $endpoint === 'sendFileByUrl') {
            return isset($data['idMessage']) || isset($data['data']['idMessage']);
        }

        if ($endpoint === 'addGroupParticipant') {
            return !isset($data['error']) && !isset($data['data']['error']);
        }

        if ($endpoint === 'getGroupData') {
            return !isset($data['error']) && !isset($data['data']['error']);
        }

        return true;
    }

    private function failureMessage(string $endpoint, array $data): string
    {
        $message = $data['message'] ?? $data['data']['message'] ?? $data['reason'] ?? null;
        if (is_string($message) && trim($message) !== '') {
            return $message;
        }

        if ($endpoint === 'sendMessage' || $endpoint === 'sendFileByUrl') {
            return 'Green API n’a pas confirmé la création du message.';
        }

        return 'Green API a refusé la requête.';
    }

    private function safePayloadForLog(array $payload): array
    {
        if (isset($payload['message']) && is_string($payload['message'])) {
            $payload['message'] = mb_substr($payload['message'], 0, 600);
        }

        if (isset($payload['caption']) && is_string($payload['caption'])) {
            $payload['caption'] = mb_substr($payload['caption'], 0, 300);
        }

        return $payload;
    }

    private function looksAlreadyInGroup(string $message, mixed $data): bool
    {
        $raw = mb_strtolower($message.' '.json_encode($data, JSON_UNESCAPED_UNICODE));

        return str_contains($raw, 'already') || str_contains($raw, 'exist') || str_contains($raw, 'in group') || str_contains($raw, 'déjà');
    }
}
