<?php
// APK Signer Class for Telegram Bot

class APKSigner {
    private $botToken;
    private $telegramApiUrl;
    private $chatId;
    private $messageId;

    public function __construct($botToken) {
        $this->botToken = $botToken;
        $this->telegramApiUrl = "https://api.telegram.org/bot" . $botToken;
    }
    /**
     * Handles the uploaded APK file
     * @param string $fileId Telegram File ID
     * @param int $chatId User's Chat ID
     * @param string $originalFileName The original name of the uploaded file
     */
    public function handleUploadedAPK($fileId, $chatId, $originalFileName) {
        // Step 1: Get the file path from Telegram
        $fileDetails = $this->getFileDetails($fileId);
        if (!$fileDetails || !isset($fileDetails['file_path'])) {
            $this->sendMessage($chatId, "❌ خطا در دریافت اطلاعات فایل از تلگرام.");
            return;
        }
        $telegramFilePath = $fileDetails['file_path'];

        // Step 2: Download the file from Telegram to a temporary location
        $downloadedFilePath = $this->downloadFile($telegramFilePath, $originalFileName);
        if (!$downloadedFilePath) {
            $this->sendMessage($chatId, "❌ خطا در دانلود فایل.");
            return;
        }

        // Step 3: Send the method selection menu (Glass Buttons!)
        $this->sendSigningMethodKeyboard($chatId, $downloadedFilePath, $originalFileName);
    }

    /**
     * Gets file details using Telegram Bot API
     * @param string $fileId
     * @return array|false
     */
    private function getFileDetails($fileId) {
        $url = $this->telegramApiUrl . "/getFile?file_id=" . urlencode($fileId);
        $response = file_get_contents($url);
        $responseData = json_decode($response, true);
        if ($responseData['ok']) {
            return $responseData['result'];
        }
        return false;
    }

    /**
     * Downloads the file from Telegram servers
     * @param string $telegramFilePath
     * @param string $localFileName
     * @return string|false Path to the downloaded file on success, false on failure.
     */
    private function downloadFile($telegramFilePath, $localFileName) {
        $fileUrl = "https://api.telegram.org/file/bot" . $this->botToken . "/" . $telegramFilePath;
        $fileContent = file_get_contents($fileUrl);

        if ($fileContent === false) {
            return false;
        }

        $tempFilePath = TEMP_DIR . '/' . uniqid('apk_') . '_' . $localFileName;
        if (file_put_contents($tempFilePath, $fileContent) !== false) {
            return $tempFilePath;
        } else {
            return false;
        }
    }

    /**
     * Sends the inline keyboard to choose a signing method
     * @param int $chatId
     * @param string $filePath Local path to the downloaded APK
     * @param string $originalFileName
     */
    /**
     * Sends the inline keyboard to choose a signing method
     */
    private function sendSigningMethodKeyboard($chatId, $filePath, $originalFileName) {
        global $SIGNING_METHODS;

        $message = "✅ فایل <code>$originalFileName</code> دریافت شد!\n\n";
        $message .= "📜 لطفاً یک روش امضا از زیر انتخاب کن:\n\n";
        
        // اضافه کردن توضیحات روش‌ها
        $message .= "📜 توضیحات روش‌ها\n";
        foreach ($SIGNING_METHODS as $methodKey => $methodInfo) {
            $message .= $methodInfo['name'] . "\n";
            $message .= $methodInfo['description'] . "\n\n";
        }
        
        $message .= "📌 نکته\n";
        $message .= "بسته به نوع دیوایس و نسخه اندروید، کارایی متدها متفاوته.\n";
        $message .= "در بسیاری موارد، استفاده از روش Combo بهترین نتیجه رو خواهد داشت.\n\n";
        $message .= "🤖 توسعه دهنده: " . CREATOR_ID;

        $inlineKeyboard = ['inline_keyboard' => []];
        $row = [];

        foreach ($SIGNING_METHODS as $methodKey => $methodInfo) {
            $callbackData = "sign_method:$methodKey:" . basename($filePath);
            $row[] = ['text' => $methodInfo['name'], 'callback_data' => $callbackData];

            // Add buttons in rows of 2 for better layout
            if (count($row) == 2) {
                $inlineKeyboard['inline_keyboard'][] = $row;
                $row = [];
            }
        }
        
        // Add the last row if it has buttons
        if (!empty($row)) {
            $inlineKeyboard['inline_keyboard'][] = $row;
        }

        $data = [
            'chat_id' => $chatId,
            'text' => $message,
            'parse_mode' => 'HTML',
            'reply_markup' => json_encode($inlineKeyboard)
        ];

        $this->sendApiRequest('sendMessage', $data);
    }

    /**
     * Processes the callback when a user selects a signing method
     */
    public function handleSigningCallback($chatId, $callbackData) {
        $parts = explode(':', $callbackData, 3);
        if (count($parts) < 3 || $parts[0] !== 'sign_method') {
            $this->sendMessage($chatId, "❌ داده callback نامعتبر.");
            return;
        }

        $methodKey = $parts[1];
        $fileName = $parts[2];
        $tempFilePath = TEMP_DIR . '/' . $fileName;

        global $SIGNING_METHODS;
        if (!array_key_exists($methodKey, $SIGNING_METHODS)) {
            $this->sendMessage($chatId, "❌ روش امضا انتخاب شده معتبر نیست.");
            return;
        }

        $selectedMethod = $SIGNING_METHODS[$methodKey];
        $this->chatId = $chatId;

        // Send initial progress message
        $progressMessage = $this->sendMessage($chatId, "⏳ شروع فرآیند امضا با " . $selectedMethod['name'] . "...\n\n📊 پیشرفت: 0%");
        $this->messageId = $progressMessage['result']['message_id'];

        // Step 1: Update progress to 25%
        $this->updateProgress(25, "🔧 در حال آماده‌سازی فایل...");

        // Step 2: Update progress to 50%
        $this->updateProgress(50, "🔐 در حال امضای فایل با " . $selectedMethod['name'] . "...");

        // Step 3: Sign the APK with the selected method
        $signedApkPath = $this->signAPK($tempFilePath, $selectedMethod, $chatId, $methodKey);

        // Step 4: Update progress to 75%
        $this->updateProgress(75, "⚡ در حال بهینه‌سازی فایل...");

        if ($signedApkPath && file_exists($signedApkPath)) {
            // Step 5: Update progress to 100%
            $this->updateProgress(100, "✅ امضا کامل شد! در حال ارسال فایل...");

            // Send the signed file
            $sendResult = $this->sendDocument($chatId, $signedApkPath, "✅ فایل شما با " . $selectedMethod['name'] . " امضا شد!\n\n" . $selectedMethod['description'] . "\n\n🤖 توسعه دهنده: " . CREATOR_ID);

            if ($sendResult) {
                // Clean up temporary files on success
                if (file_exists($tempFilePath)) {
                    unlink($tempFilePath);
                }
                if (file_exists($signedApkPath)) {
                    unlink($signedApkPath);
                }
                
                // Delete progress message after successful send
                $this->deleteMessage($chatId, $this->messageId);
            } else {
                $this->sendMessage($chatId, "❌ فایل امضا شده اما در ارسال آن مشکل پیش آمد. لطفاً با پشتیبانی تماس بگیرید.");
            }

        } else {
            $this->updateProgress(0, "❌ خطا در فرآیند امضا!");
            $this->sendMessage($chatId, "❌ متأسفانه خطایی در هنگام امضای فایل رخ داد. لطفاً دوباره سعی کنید یا با پشتیبانی تماس بگیرید.");
        }
    }

    /**
     * Signs the APK using the selected method
     */
    private function signAPK($apkPath, $methodInfo, $chatId, $methodKey) {
        $signedApkName = 'signed_' . $methodKey . '_' . time() . '_' . basename($apkPath);
        $signedApkPath = SIGNED_APKS_DIR . '/' . $signedApkName;

        //根据不同方法执行不同的签名命令
        switch ($methodKey) {
            case 'resign':
                // استفاده از jarsigner برای روش ReSign
                $keystore = $methodInfo['keystore'];
                $password = $methodInfo['password'];
                $alias = $methodInfo['alias'];

                $tempUnsignedApk = TEMP_DIR . '/unsigned_' . basename($apkPath);
                copy($apkPath, $tempUnsignedApk);

                $signCommand = "jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 ";
                $signCommand .= "-keystore " . escapeshellarg($keystore) . " ";
                $signCommand .= "-storepass " . escapeshellarg($password) . " ";
                $signCommand .= "-signedjar " . escapeshellarg($signedApkPath) . " ";
                $signCommand .= escapeshellarg($tempUnsignedApk) . " ";
                $signCommand .= escapeshellarg($alias) . " 2>&1";

                $output = shell_exec($signCommand);
                $returnVar = $output === null ? 1 : 0;

                unlink($tempUnsignedApk);

                if ($returnVar !== 0) {
                    $errorMessage = "❌ خطای jarsigner: " . ($output ? $output : 'خطای ناشناخته');
                    $this->sendMessage($chatId, $errorMessage);
                    file_put_contents(LOGS_DIR . '/signing_errors.log', date('Y-m-d H:i:s') . " - ChatID: $chatId - Command: $signCommand - Output: " . ($output ? $output : 'No output') . "\n", FILE_APPEND);
                    return false;
                }
                break;

            case 'malform':
            case 'zip_trick':
            case 'combo':
                // برای سایر روش‌ها، از ابزارهای خاص استفاده خواهد شد
                // در اینجا فقط یک کپی ساده انجام می‌دهیم (این بخش باید با ابزارهای واقعی جایگزین شود)
                if (!copy($apkPath, $signedApkPath)) {
                    $this->sendMessage($chatId, "❌ خطا در پردازش فایل با " . $methodInfo['name']);
                    return false;
                }
                break;

            default:
                $this->sendMessage($chatId, "❌ روش امضای ناشناخته: " . $methodKey);
                return false;
        }

        return file_exists($signedApkPath) ? $signedApkPath : false;
    }
    /**
     * Deletes a message
     */
    private function deleteMessage($chatId, $messageId) {
        $data = [
            'chat_id' => $chatId,
            'message_id' => $messageId
        ];
        $this->sendApiRequest('deleteMessage', $data);
    }

    /**
     * Sends a document (the signed APK) back to the user
     * @param int $chatId
     * @param string $documentPath Local path to the file to send
     * @param string $caption Optional caption for the file
     * @return bool True on success, false on failure
     */
    private function sendDocument($chatId, $documentPath, $caption = '') {
        $url = $this->telegramApiUrl . "/sendDocument";

        // Check if file exists and is readable
        if (!file_exists($documentPath) || !is_readable($documentPath)) {
            $this->sendMessage($chatId, "❌ فایل امضا شده وجود ندارد یا قابل دسترسی نیست.");
            file_put_contents(LOGS_DIR . '/send_errors.log', date('Y-m-d H:i:s') . " - File not found: $documentPath\n", FILE_APPEND);
            return false;
        }

        // Check file size (Telegram limit is 50MB for bots)
        $fileSize = filesize($documentPath);
        if ($fileSize > 50 * 1024 * 1024) {
            $sizeInMB = round($fileSize / (1024 * 1024), 2);
            $this->sendMessage($chatId, "❌ حجم فایل امضا شده ({$sizeInMB} مگابایت) از حد مجاز تلگرام (50 مگابایت) بیشتر است.");
            file_put_contents(LOGS_DIR . '/send_errors.log', date('Y-m-d H:i:s') . " - File too large: $documentPath ({$sizeInMB}MB)\n", FILE_APPEND);
            return false;
        }

        // Prepare POST data using CURLFile for file upload
        $postData = [
            'chat_id' => $chatId,
            'caption' => $caption,
            'parse_mode' => 'HTML',
            'document' => new CURLFile(realpath($documentPath))
        ];

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Increase timeout to 2 minutes for large files
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: multipart/form-data'
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        
        curl_close($ch);

        // Log the complete response for debugging
        file_put_contents(LOGS_DIR . '/send_document.log', date('Y-m-d H:i:s') . " - File: $documentPath - Size: " . round($fileSize / (1024 * 1024), 2) . "MB - HTTP Code: $httpCode - Error: $error\n", FILE_APPEND);

        if ($httpCode !== 200) {
            $this->sendMessage($chatId, "❌ خطا در ارسال فایل امضا شده. کد خطا: $httpCode");
            return false;
        }
        
        $responseData = json_decode($response, true);
        if (!$responseData || !$responseData['ok']) {
            $errorDescription = $responseData['description'] ?? 'Unknown error';
            $this->sendMessage($chatId, "❌ خطای تلگرام در ارسال فایل: $errorDescription");
            return false;
        }

        return true;
    }

    /**
     * A helper function to send messages
     * @param int $chatId
     * @param string $text
     * @return array API response
     */
    public function sendMessage($chatId, $text) {
        $data = [
            'chat_id' => $chatId,
            'text' => $text,
            'parse_mode' => 'HTML'
        ];
        return $this->sendApiRequest('sendMessage', $data);
    }

    /**
     * Sends a request to the Telegram Bot API
     * @param string $method API Method (e.g., 'sendMessage')
     * @param array $data Parameters for the method
     * @return mixed Decoded JSON response or false on failure.
     */
    private function sendApiRequest($method, $data) {
        $url = $this->telegramApiUrl . '/' . $method;
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        $response = curl_exec($ch);
        curl_close($ch);
        return json_decode($response, true);
    }
}
?>