<?php

class DiscordSender
{
    private const API_BASE_URL = 'https://discord.com/api/v10';
    private const MESSAGE_CHAR_LIMIT = 1990;
    private const LOG_FILE = __DIR__ . '/../logs/discord_api.log';

    private string $token;

    public function __construct(string $token)
    {
        custom_log('[DiscordSender] Initializing...');
        $this->token = $token;
    }

    public function sendMessage(string $discordId, string $content, string $recipientType = 'channel') {
        custom_log("[DiscordSender] sendMessage: Called for ID {$discordId} and recipient type {$recipientType}.");
        
        try {
            if (empty(trim($content))) {
                $this->logMessage("Error: No se puede enviar un mensaje vacío");
                return false;
            }
            
            $targetChannelId = $this->getTargetChannelId($discordId, $recipientType);
            custom_log("[DiscordSender] sendMessage: Target channel ID is {$targetChannelId}.");
            
            $parts = [];
            
            preg_match_all('/<img[^>]+src=[\'"]([^\'"]+)[\'"][^>]*>/i', $content, $imageMatches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
            
            $lastPos = 0;
            
            foreach ($imageMatches as $match) {
                $imageTag = $match[0][0];
                $imageUrl = $match[1][0];
                $imagePos = $match[0][1];
                
                $textBefore = trim(substr($content, $lastPos, $imagePos - $lastPos));
                if (!empty($textBefore)) {
                    $textWithNewlines = str_ireplace(['<p>', '</p>', '<br>', '<br />'], ["", "\n", "\n", "\n"], $textBefore);
                    $text = trim(strip_tags($textWithNewlines));
                    if (!empty($text)) {
                        $parts[] = ['type' => 'text', 'content' => $text];
                    }
                }
                
                if (!empty($imageUrl)) {
                    $absoluteImageUrl = $imageUrl;
                    if (strpos($imageUrl, 'http') !== 0 && strpos($imageUrl, '//') !== 0) {
                        $base = rtrim(BOT_BASE_URL, '/');
                        $path = ltrim($imageUrl, '/');
                        $absoluteImageUrl = "{$base}/{$path}";
                    }
                    $parts[] = ['type' => 'image', 'url' => $absoluteImageUrl];
                }
                
                $lastPos = $imagePos + strlen($imageTag);
            }
            
            $textAfter = trim(substr($content, $lastPos));
            if (!empty($textAfter)) {
                $textWithNewlines = str_ireplace(['<p>', '</p>', '<br>', '<br />'], ["", "\n", "\n", "\n"], $textAfter);
                $text = trim(strip_tags($textWithNewlines));
                if (!empty($text)) {
                    $parts[] = ['type' => 'text', 'content' => $text];
                }
            }
            
            if (empty($parts)) {
                 $textWithNewlines = str_ireplace(['<p>', '</p>', '<br>', '<br />'], ["", "\n", "\n", "\n"], $content);
                 $text = trim(strip_tags($textWithNewlines));
                 if (!empty($text)) {
                    $parts[] = ['type' => 'text', 'content' => $text];
                 }
            }

            if (empty($parts)) {
                return false;
            }
            
            $messageIds = [];
            $allPartsSentSuccessfully = true;
            
            foreach ($parts as $part) {
                if ($part['type'] === 'text') {
                    $chunks = $this->splitMessage($part['content']);
                    foreach ($chunks as $chunk) {
                        $trimmedChunk = trim($chunk);
                        if ($trimmedChunk === '') continue;

                        try {
                            $response = $this->sendDiscordMessage($targetChannelId, ['content' => $trimmedChunk]);
                            if (isset($response['id'])) {
                                $messageIds[] = $response['id'];
                            } else {
                                $allPartsSentSuccessfully = false; break;
                            }
                        } catch (Exception $e) {
                            $this->logMessage("Error al enviar texto: " . $e->getMessage());
                            $allPartsSentSuccessfully = false; break;
                        }
                        usleep(250000);
                    }
                } elseif ($part['type'] === 'image') {
                    try {
                        $response = $this->sendDiscordMessage($targetChannelId, ['content' => $part['url']]);
                         if (isset($response['id'])) {
                            $messageIds[] = $response['id'];
                        } else {
                            $allPartsSentSuccessfully = false; break;
                        }
                    } catch (Exception $e) {
                        $this->logMessage("Error al enviar imagen como URL: " . $e->getMessage());
                        $allPartsSentSuccessfully = false; break;
                    }
                }
                if (!$allPartsSentSuccessfully) break;
                usleep(500000);
            }
            
            return $allPartsSentSuccessfully ? $messageIds : false;
            
        } catch (Exception $e) {
            $this->logMessage("Error in sendMessage: " . $e->getMessage());
            throw $e;
        }
    }
    
    private function getTargetChannelId(string $discordId, string $recipientType): string {
        if ($recipientType === 'user') {
            return $this->createDMChannel($discordId);
        }
        return $discordId;
    }
    
    private function createDMChannel(string $userId): string {
        $url = self::API_BASE_URL . '/users/@me/channels';
        $data = json_encode(['recipient_id' => $userId]);
        
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_HTTPHEADER => [
                'Authorization: Bot ' . $this->token,
                'Content-Type: application/json',
                'Content-Length: ' . strlen($data)
            ],
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => $data
        ]);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        
        if (curl_errno($ch)) {
            throw new Exception('cURL error: ' . curl_error($ch));
        }
        
        curl_close($ch);
        
        if ($httpCode !== 200) {
            throw new Exception("Failed to create DM channel. HTTP code: $httpCode, Response: $response");
        }
        
        $responseData = json_decode($response, true);
        return $responseData['id'];
    }
    
    private function sendDiscordMessage(string $channelId, array $payload, array $files = []) {
        $url = self::API_BASE_URL . "/channels/{$channelId}/messages";
        
        if (isset($payload['content'])) {
            $payload['content'] = trim($payload['content']);
            if ($payload['content'] === '') unset($payload['content']);
        }
        
        if (empty($payload['content'] ?? '') && empty($payload['embeds'] ?? '') && empty($files)) {
            throw new Exception("No se puede enviar un mensaje vacío");
        }
        
        $ch = curl_init($url);
        $headers = ['Authorization: Bot ' . $this->token, 'User-Agent: DiscordBot (v1.0)'] ;
        
        if (empty($files)) {
            $headers[] = 'Content-Type: application/json';
            $postData = json_encode($payload);
        } else {
            // Multipart logic for files would go here if needed
        }
        
        curl_setopt_array($ch, [
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => $postData
        ]);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        $responseData = json_decode($response, true);
        
        if ($httpCode >= 400) {
            throw new Exception("Discord API error ({$httpCode}): " . ($responseData['message'] ?? 'Unknown error'));
        }
        
        return $responseData;
    }

    private function splitMessage(string $text, int $maxLength = self::MESSAGE_CHAR_LIMIT): array
    {
        $chunks = [];
        $text = str_replace(["\r\n", "\r"], "\n", $text);
        $lines = explode("\n", $text);
        $currentChunk = '';

        foreach ($lines as $line) {
            if (mb_strlen($currentChunk, 'UTF-8') + mb_strlen($line, 'UTF-8') + 1 > $maxLength) {
                $chunks[] = $currentChunk;
                $currentChunk = $line;
            } else {
                $currentChunk .= (empty($currentChunk) ? '' : "\n") . $line;
            }
        }
        if (!empty($currentChunk)) $chunks[] = $currentChunk;

        return $chunks;
    }

    private function logMessage(string $message): void {
        $logMessage = date('[Y-m-d H:i:s] ') . $message . "\n";
        file_put_contents(self::LOG_FILE, $logMessage, FILE_APPEND | LOCK_EX);
    }

    public function sendRawMessage(string $channelId, string $content): ?array
    {
        custom_log("[DiscordSender] sendRawMessage: Called for channel ID {$channelId}.");
        try {
            return $this->sendDiscordMessage($channelId, ['content' => $content]);
        } catch (Exception $e) {
            $this->logMessage("Error in sendRawMessage: " . $e->getMessage());
            return null;
        }
    }

    /**
     * Envía un Embed (construido como un array) a un canal de Discord.
     *
     * @param string $channelId El ID del canal de destino.
     * @param array $embedData El array que representa el embed.
     * @return array|null La respuesta de la API de Discord o null si hay un error.
     */
    public function sendEmbedData(string $channelId, array $embedData): ?array
    {
        custom_log("[DiscordSender] sendEmbedData: Called for channel ID {$channelId}.");
        try {
            return $this->sendDiscordMessage($channelId, ['embeds' => [$embedData]]);
        } catch (Exception $e) {
            $this->logMessage("Error in sendEmbedData: " . $e->getMessage());
            return null;
        }
    }
}