<?php
declare(strict_types=1);

// Transaction handling functions
function extractTransactionHash(string $text): array
{
    $trimmed = trim($text);
    
    // Only process full URLs, ignore everything else
    if (!preg_match('~https?://~', $trimmed)) {
        return [null, null];
    }
    
    // TON: https://tonviewer.com/transaction/...
    if (preg_match('~https?://[^/]*tonviewer\.com/transaction/([A-Za-z0-9_-]{44,66})~i', $trimmed, $match)) {
        return [$match[1], 'ton'];
    }
    
    // TRON: https://tronscan.org/#/transaction/... or https://tronscan.io/#/transaction/...
    if (preg_match('~https?://[^/]*tronscan\.(org|io)/[^#]*#/transaction/([A-Fa-f0-9]{64})~i', $trimmed, $match)) {
        return [$match[2], 'tron'];
    }
    // Also try without fragment (direct path)
    if (preg_match('~https?://[^/]*tronscan\.(org|io)/transaction/([A-Fa-f0-9]{64})~i', $trimmed, $match)) {
        return [$match[2], 'tron'];
    }

    return [null, null];
}

function handleTransactionLookup(string $txid, ?string $networkHint, array $config, int $chatId): bool
{
    // Only check the specific network based on URL
    if ($networkHint === 'tron') {
        $tron = fetchTronTransaction($txid, $config);
        if ($tron) {
            $message = formatTronMessage($tron, $config);
            
            // Add inline keyboard with channel button
            $inlineKeyboard = [
                [
                    [
                        'text' => "پل واسطه | PolVasete",
                        'url' => "https://t.me/PolVasete"
                    ]
                ]
            ];
            
            sendTelegram('sendMessage', [
                'chat_id' => $chatId,
                'parse_mode' => 'HTML',
                'text' => $message,
                'reply_markup' => json_encode(['inline_keyboard' => $inlineKeyboard]),
            ], $config);
            return true;
        } else {
            $errorText = sprintf("❌ تراکنش TRON یافت نشد.\nهش: <code>%s</code>", htmlspecialchars($txid));
            sendTelegram('sendMessage', [
                'chat_id' => $chatId,
                'parse_mode' => 'HTML',
                'text' => $errorText,
            ], $config);
            return true;
        }
    }

    if ($networkHint === 'ton') {
        $tonTx = fetchTonTransaction($txid, $config);
        if ($tonTx) {
            $message = formatTonMessage($tonTx, $config);
            
            // Add inline keyboard with channel button
            $inlineKeyboard = [
                [
                    [
                        'text' => "پل واسطه | PolVasete",
                        'url' => "https://t.me/PolVasete"
                    ]
                ]
            ];
            
            sendTelegram('sendMessage', [
                'chat_id' => $chatId,
                'parse_mode' => 'HTML',
                'text' => $message,
                'reply_markup' => json_encode(['inline_keyboard' => $inlineKeyboard]),
            ], $config);
            return true;
        } else {
            $errorText = sprintf("❌ تراکنش TON یافت نشد.\nهش: <code>%s</code>", htmlspecialchars($txid));
            sendTelegram('sendMessage', [
                'chat_id' => $chatId,
                'parse_mode' => 'HTML',
                'text' => $errorText,
            ], $config);
            return true;
        }
    }

    return false;
}

// Utility functions
function ensureDirectories(array $config): void
{
    foreach (['logs', 'storage', 'storage/charts'] as $dir) {
        $full = __DIR__ . '/' . $dir;
        if (!is_dir($full)) {
            mkdir($full, 0775, true);
        }
    }
    $files = [
        $config['CACHE_FILE'] ?? (__DIR__ . '/storage/cache.json'),
    ];
    foreach ($files as $file) {
        if (!file_exists($file)) {
            file_put_contents($file, '{}');
        }
    }
}

function logLine(array $config, string $line): void
{
    file_put_contents($config['LOG_FILE'], date('c') . ' ' . $line . PHP_EOL, FILE_APPEND);
}

function logError(array $config, string $line): void
{
    file_put_contents($config['ERROR_LOG_FILE'], date('c') . ' ' . $line . PHP_EOL, FILE_APPEND);
}

function fetchCardInfo(string $cardNumber, array $config): ?array
{
    $apiKey = "dng1TP95DczlOmDayHHSrOdTjyloPBayD3V0maj8QpAMAJEcnvkhPKOJSzdU9iIiOdy483rJ0n2G6UDlYPgmHxK5rkwv9MtpGxMCPLbW5eXiSpN10todQFhyMTu3GuaoQvCCeClJjyjfruWIiNmGFS";
    
    $curl = curl_init();
    curl_setopt_array($curl, [
        CURLOPT_URL => "https://shabanic.ir/api/v1/card_info?card_no={$cardNumber}",
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_ENCODING => '',
        CURLOPT_MAXREDIRS => 10,
        CURLOPT_TIMEOUT => 0,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        CURLOPT_CUSTOMREQUEST => 'POST',
        CURLOPT_HTTPHEADER => [
            "SHABANIC-API-KEY: $apiKey"
        ],
    ]);
    $response = curl_exec($curl);
    $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
    curl_close($curl);
    
    if ($httpCode !== 200) {
        logError($config, "Card info API returned HTTP $httpCode for card: $cardNumber");
        return null;
    }
    
    $data = @json_decode($response, true);
    
    if (
        $data &&
        isset($data['status']) && $data['status'] === 'DONE' &&
        isset($data['result']) && is_array($data['result'])
    ) {
        return $data['result'];
    }
    
    return null;
}

function sendTelegram(string $method, array $params, array $config): bool
{
    $url = $config['TELEGRAM_API'] . $config['BOT_TOKEN'] . '/' . $method;
    $response = httpRequest($url, 'POST', $params);
    if (!$response || empty($response['ok'])) {
        $description = $response['description'] ?? 'Unknown Telegram API error';
        logError($config, sprintf('Telegram %s failed: %s', $method, $description));
        return false;
    }
    return true;
}

function httpRequest(string $url, string $method = 'GET', ?array $params = null, array $headers = [], bool $asJson = false): ?array
{
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    $httpHeaders = ['Accept: application/json'];

    if ($method === 'POST') {
        curl_setopt($ch, CURLOPT_POST, true);
        if ($asJson) {
            $payload = json_encode($params ?? [], JSON_UNESCAPED_UNICODE);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $payload ?: '{}');
            $httpHeaders[] = 'Content-Type: application/json';
        } else {
            $hasFile = false;
            if ($params) {
                foreach ($params as $key => $value) {
                    if ($value instanceof CURLFile || (is_string($value) && file_exists($value) && strpos($key, 'photo') !== false)) {
                        $hasFile = true;
                        break;
                    }
                }
            }
            
            if ($hasFile) {
                foreach ($params as $key => $value) {
                    if (is_string($value) && file_exists($value) && strpos($key, 'photo') !== false) {
                        $params[$key] = new CURLFile($value, 'image/png', basename($value));
                    }
                }
                curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
            } else {
                curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params ?? []));
            }
        }
    } elseif ($params) {
        $url .= (str_contains($url, '?') ? '&' : '?') . http_build_query($params);
        curl_setopt($ch, CURLOPT_URL, $url);
    }

    if ($headers) {
        $httpHeaders = array_merge($httpHeaders, $headers);
    }
    curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeaders);

    $response = curl_exec($ch);
    if ($response === false) {
        curl_close($ch);
        return null;
    }

    curl_close($ch);
    $decoded = json_decode($response, true);
    return $decoded ?? ['raw' => $response];
}

function isFlood(array $config, int $chatId, int $userId): bool
{
    $cacheFile = $config['CACHE_FILE'];
    $cache = json_decode(file_get_contents($cacheFile), true) ?: [];
    $key = $chatId . '_' . $userId;
    $now = time();
    $window = $config['ANTISPAM']['window_seconds'];
    $max = $config['ANTISPAM']['max_hits'];
    $entry = $cache[$key] ?? ['count' => 0, 'reset' => $now + $window];

    if ($entry['reset'] <= $now) {
        $entry = ['count' => 0, 'reset' => $now + $window];
    }
    $entry['count']++;
    $cache[$key] = $entry;

    file_put_contents($cacheFile, json_encode($cache));
    return $entry['count'] > $max;
}

function containsKeyword(string $text, array $keywords): bool
{
    foreach ($keywords as $keyword) {
        $needle = mb_strtolower($keyword, 'UTF-8');
        if ($needle !== '' && mb_strpos($text, $needle) !== false) {
            return true;
        }
    }
    return false;
}

// Transaction fetch functions
function fetchTronTransaction(string $txid, array $config): ?array
{
    $payload = ['value' => $txid];
    $txData = httpRequest($config['TRON']['tx_endpoint'], 'POST', $payload, [], true);
    $infoData = httpRequest($config['TRON']['info_endpoint'], 'POST', $payload, [], true);
    if (!$txData || !$infoData || !isset($txData['raw_data'])) {
        logError($config, 'TronGrid lookup failed for tx ' . $txid);
        return null;
    }

    $contract = $txData['raw_data']['contract'][0]['parameter']['value'] ?? [];
    $amountSun = $contract['amount'] ?? 0;
    $amountTrx = $amountSun / 1_000_000;

    $usdPrice = fetchTrxUsd($config);
    $usdAmount = $usdPrice ? $amountTrx * $usdPrice : null;

    // Extract status - check multiple possible locations
    $receiptResult = $infoData['receipt']['result'] ?? null;
    if ($receiptResult === 'SUCCESS' || $receiptResult === 0 || $receiptResult === '0') {
        $status = 'SUCCESS';
    } elseif ($receiptResult !== null) {
        $status = strtoupper((string)$receiptResult);
    } elseif (isset($infoData['receipt']['ret']) && $infoData['receipt']['ret'] === 'SUCCESS') {
        $status = 'SUCCESS';
    } elseif (isset($txData['ret']) && $txData['ret'][0]['contractRet'] === 'SUCCESS') {
        $status = 'SUCCESS';
    } else {
        $status = 'UNKNOWN';
    }
    
    // Extract fee - check multiple possible locations
    $feeTrx = 0;
    if (isset($infoData['fee'])) {
        $feeTrx = ($infoData['fee'] ?? 0) / 1_000_000;
    } elseif (isset($infoData['receipt']['fee'])) {
        $feeTrx = ($infoData['receipt']['fee'] ?? 0) / 1_000_000;
    } elseif (isset($infoData['receipt']['energy_fee'])) {
        $feeTrx = (($infoData['receipt']['energy_fee'] ?? 0) + ($infoData['receipt']['net_fee'] ?? 0)) / 1_000_000;
    }
    
    $blockNumber = $infoData['blockNumber'] ?? 0;
    $timestamp = ($infoData['blockTimeStamp'] ?? 0) / 1000;
    
    // Extract contract type - map to readable name
    $contractTypeRaw = $txData['raw_data']['contract'][0]['type'] ?? 'Unknown';
    $contractTypeMap = [
        'TransferContract' => 'TransferContract',
        'TransferAssetContract' => 'TransferAssetContract',
        'TriggerSmartContract' => 'SmartContract',
        'CreateSmartContract' => 'CreateSmartContract',
    ];
    $contractType = $contractTypeMap[$contractTypeRaw] ?? $contractTypeRaw;

    return [
        'status' => $status,
        'amount_trx' => $amountTrx,
        'amount_usd' => $usdAmount,
        'from' => formatTronAddress($contract['owner_address'] ?? ''),
        'to' => formatTronAddress($contract['to_address'] ?? ''),
        'fee' => $feeTrx,
        'block' => $blockNumber,
        'timestamp' => $timestamp,
        'contract_type' => $contractType,
        'txid' => $txid,
    ];
}

function formatTronMessage(array $tx, array $config): string
{
    $amountUsd = $tx['amount_usd'] ? sprintf(' ($%s)', number_format((float)$tx['amount_usd'], 2)) : '';
    $time = $tx['timestamp'] ? date('Y-m-d H:i:s', (int)$tx['timestamp']) : '-';
    
    // Ensure all values are strings before passing to htmlspecialchars
    $status = is_array($tx['status'] ?? null) ? json_encode($tx['status']) : (string)($tx['status'] ?? 'UNKNOWN');
    $from = is_array($tx['from'] ?? null) ? json_encode($tx['from']) : (string)($tx['from'] ?? '-');
    $to = is_array($tx['to'] ?? null) ? json_encode($tx['to']) : (string)($tx['to'] ?? '-');
    $contractType = is_array($tx['contract_type'] ?? null) ? json_encode($tx['contract_type']) : (string)($tx['contract_type'] ?? '-');
    $block = is_array($tx['block'] ?? null) ? json_encode($tx['block']) : (string)($tx['block'] ?? '-');
    
    return sprintf(
        "🔍 وضعیت تراکنش TRX\n\n📌 وضعیت: <b>%s</b>\n\n💸 مقدار: %s TRX %s\n\n👤 فرستنده: <code>%s</code>\n\n🎯 گیرنده: <code>%s</code>\n\n⛽ کارمزد: %s TRX\n⛓ Block: %s\n🕒 زمان: %s\nنوع قرارداد: %s",
        htmlspecialchars($status),
        number_format($tx['amount_trx'] ?? 0, 6),
        $amountUsd,
        htmlspecialchars($from),
        htmlspecialchars($to),
        number_format($tx['fee'] ?? 0, 6),
        htmlspecialchars($block),
        $time,
        htmlspecialchars($contractType)
    );
}

function fetchTonTransaction(string $hash, array $config): ?array
{
    $base = rtrim($config['TON']['api_base'], '/');
    $headers = [];
    if (!empty($config['TON']['api_key'])) {
        $headers[] = 'Authorization: Bearer ' . $config['TON']['api_key'];
    }
    $response = httpRequest($base . '/' . $hash, 'GET', null, $headers);
    if (!$response) {
        logError($config, 'TON transaction lookup failed for hash ' . $hash);
        return null;
    }

    $payload = $response['result'] ?? $response['transaction'] ?? $response;
    if (!is_array($payload)) {
        logError($config, 'TON transaction payload invalid for ' . $hash);
        return null;
    }

    // Extract status - check multiple possible locations
    $status = 'UNKNOWN';
    if (isset($payload['status'])) {
        $status = strtoupper((string)$payload['status']);
    } elseif (isset($payload['final_state'])) {
        $status = strtoupper((string)$payload['final_state']);
    } elseif (isset($payload['success']) && $payload['success'] === true) {
        $status = 'SUCCESS';
    } elseif (isset($payload['actions']) && is_array($payload['actions']) && count($payload['actions']) > 0) {
        $status = 'SUCCESS'; // If actions exist, transaction is likely successful
    }
    
    // Extract amount - check multiple locations more thoroughly
    $valueNanoton = 0;
    
    // First, try in_msg.value (most common location)
    if (isset($payload['in_msg']) && is_array($payload['in_msg'])) {
        if (isset($payload['in_msg']['value'])) {
            $valueNanoton = (float)$payload['in_msg']['value'];
        }
    }
    
    // If still 0, try actions (for transactions with multiple transfers)
    if ($valueNanoton == 0 && isset($payload['actions']) && is_array($payload['actions'])) {
        foreach ($payload['actions'] as $action) {
            if (isset($action['type']) && $action['type'] === 'TonTransfer') {
                if (isset($action['TonTransfer']['amount'])) {
                    $valueNanoton += (float)$action['TonTransfer']['amount'];
                }
            } elseif (isset($action['TonTransfer']) && is_array($action['TonTransfer'])) {
                if (isset($action['TonTransfer']['amount'])) {
                    $valueNanoton += (float)$action['TonTransfer']['amount'];
                }
            } elseif (isset($action['amount'])) {
                $valueNanoton += (float)$action['amount'];
            }
        }
    }
    
    // If still 0, try direct value field
    if ($valueNanoton == 0 && isset($payload['value'])) {
        $valueNanoton = (float)$payload['value'];
    }
    
    // If still 0, try out_msgs (sometimes the amount is in outgoing messages)
    if ($valueNanoton == 0 && isset($payload['out_msgs']) && is_array($payload['out_msgs'])) {
        foreach ($payload['out_msgs'] as $outMsg) {
            if (isset($outMsg['value'])) {
                $valueNanoton += (float)$outMsg['value'];
            }
        }
    }
    
    // If still 0, try in_msg without checking if it's null first
    if ($valueNanoton == 0 && isset($payload['in_msg']) && !is_null($payload['in_msg'])) {
        if (is_array($payload['in_msg']) && isset($payload['in_msg']['value'])) {
            $valueNanoton = (float)$payload['in_msg']['value'];
        } elseif (is_string($payload['in_msg']) || is_numeric($payload['in_msg'])) {
            // Sometimes in_msg might be directly the value
            $valueNanoton = (float)$payload['in_msg'];
        }
    }
    
    $amountTon = $valueNanoton / 1_000_000_000;
    
    // Log for debugging if amount is still 0 (with more details)
    if ($amountTon == 0) {
        $debugInfo = [
            'hash' => $hash,
            'has_in_msg' => isset($payload['in_msg']),
            'in_msg_value' => $payload['in_msg']['value'] ?? 'N/A',
            'has_actions' => isset($payload['actions']),
            'actions_count' => isset($payload['actions']) ? count($payload['actions']) : 0,
            'has_value' => isset($payload['value']),
            'value' => $payload['value'] ?? 'N/A',
            'payload_keys' => implode(', ', array_keys($payload))
        ];
        logError($config, 'TON transaction amount is 0 - Debug: ' . json_encode($debugInfo));
    }
    
    // Extract fee - check multiple locations
    $feeNano = 0;
    if (isset($payload['fee'])) {
        $feeNano = (float)$payload['fee'];
    } elseif (isset($payload['out_msgs']) && is_array($payload['out_msgs'])) {
        // Fee might be in out_msgs
        foreach ($payload['out_msgs'] as $outMsg) {
            if (isset($outMsg['fwd_fee'])) {
                $feeNano += (float)$outMsg['fwd_fee'];
            }
        }
    }
    
    // Extract LT
    $lt = '';
    if (isset($payload['lt'])) {
        $lt = (string)$payload['lt'];
    } elseif (isset($payload['block']['lt'])) {
        $lt = (string)$payload['block']['lt'];
    }
    
    // Extract block - ensure it's just the seqno number, not the whole array
    $block = '';
    if (isset($payload['block_id']['seqno'])) {
        $block = (string)$payload['block_id']['seqno'];
    } elseif (isset($payload['block']['seqno'])) {
        $block = (string)$payload['block']['seqno'];
    } elseif (isset($payload['block']) && is_numeric($payload['block'])) {
        $block = (string)$payload['block'];
    } elseif (is_array($payload['block'] ?? null) && isset($payload['block']['seqno'])) {
        $block = (string)$payload['block']['seqno'];
    }
    
    // Extract timestamp
    $ts = time();
    if (isset($payload['utime'])) {
        $ts = (int)$payload['utime'];
    } elseif (isset($payload['timestamp'])) {
        $ts = (int)$payload['timestamp'];
    } elseif (isset($payload['now'])) {
        $ts = (int)$payload['now'];
    }

    // Extract source and destination - prioritize in_msg (most reliable)
    $sourceRaw = null;
    $destinationRaw = null;
    
    // Try in_msg first (this is the most reliable source for TON transactions)
    if (isset($payload['in_msg'])) {
        if (isset($payload['in_msg']['source'])) {
            $sourceRaw = $payload['in_msg']['source'];
        }
        if (isset($payload['in_msg']['destination'])) {
            $destinationRaw = $payload['in_msg']['destination'];
        }
    }
    
    // Try account address if source not found
    if (!$sourceRaw && isset($payload['account']['address'])) {
        $sourceRaw = $payload['account']['address'];
    }
    
    // Try actions as fallback
    if (!$sourceRaw && isset($payload['actions']) && is_array($payload['actions'])) {
        foreach ($payload['actions'] as $action) {
            if (isset($action['TonTransfer'])) {
                if (isset($action['TonTransfer']['sender']) && !$sourceRaw) {
                    $sourceRaw = $action['TonTransfer']['sender'];
                }
                if (isset($action['TonTransfer']['recipient']) && !$destinationRaw) {
                    $destinationRaw = $action['TonTransfer']['recipient'];
                }
            }
        }
    }
    
    // Try direct fields as last resort
    if (!$sourceRaw && isset($payload['from'])) {
        $sourceRaw = $payload['from'];
    }
    if (!$destinationRaw && isset($payload['to'])) {
        $destinationRaw = $payload['to'];
    }
    
    // Convert to string if array (TON API sometimes returns address objects)
    $source = '';
    if ($sourceRaw !== null) {
        if (is_array($sourceRaw)) {
            // Try different possible keys for address
            if (isset($sourceRaw['address'])) {
                $source = (string)$sourceRaw['address'];
            } elseif (isset($sourceRaw['raw'])) {
                $source = (string)$sourceRaw['raw'];
            } elseif (isset($sourceRaw['bounceable'])) {
                $source = (string)$sourceRaw['bounceable'];
            } elseif (isset($sourceRaw['raw_form'])) {
                $source = (string)$sourceRaw['raw_form'];
            } elseif (isset($sourceRaw['user_friendly'])) {
                $source = (string)$sourceRaw['user_friendly'];
            } else {
                $source = json_encode($sourceRaw);
            }
        } else {
            $source = (string)$sourceRaw;
        }
    }
    
    $destination = '';
    if ($destinationRaw !== null) {
        if (is_array($destinationRaw)) {
            // Try different possible keys for address
            if (isset($destinationRaw['address'])) {
                $destination = (string)$destinationRaw['address'];
            } elseif (isset($destinationRaw['raw'])) {
                $destination = (string)$destinationRaw['raw'];
            } elseif (isset($destinationRaw['bounceable'])) {
                $destination = (string)$destinationRaw['bounceable'];
            } elseif (isset($destinationRaw['raw_form'])) {
                $destination = (string)$destinationRaw['raw_form'];
            } elseif (isset($destinationRaw['user_friendly'])) {
                $destination = (string)$destinationRaw['user_friendly'];
            } else {
                $destination = json_encode($destinationRaw);
            }
        } else {
            $destination = (string)$destinationRaw;
        }
    }

    return [
        'status' => $status,
        'amount' => $amountTon,
        'fee' => $feeNano / 1_000_000_000,
        'source' => $source,
        'destination' => $destination,
        'lt' => $lt,
        'block' => $block,
        'timestamp' => $ts,
        'hash' => $hash,
    ];
}

function formatTonMessage(array $tx, array $config): string
{
    $time = date('Y-m-d H:i:s', (int)$tx['timestamp']);
    
    // Ensure all values are strings before passing to htmlspecialchars
    $status = is_array($tx['status'] ?? null) ? json_encode($tx['status']) : (string)($tx['status'] ?? 'UNKNOWN');
    $source = is_array($tx['source'] ?? null) ? json_encode($tx['source']) : (string)($tx['source'] ?? '-');
    $destination = is_array($tx['destination'] ?? null) ? json_encode($tx['destination']) : (string)($tx['destination'] ?? '-');
    $block = is_array($tx['block'] ?? null) ? json_encode($tx['block']) : (string)($tx['block'] ?? '-');
    $lt = is_array($tx['lt'] ?? null) ? json_encode($tx['lt']) : (string)($tx['lt'] ?? '-');
    
    return sprintf(
        "🔹 وضعیت تراکنش TON\n\n📌 وضعیت: %s\n\n💠 مقدار: %s TON\n\n👤 From: <code>%s</code>\n\n🎯 To: <code>%s</code>\n\n⛽ Fee: %s TON\n📦 Block/LT: %s / %s\n🕒 زمان: %s",
        htmlspecialchars($status),
        number_format($tx['amount'] ?? 0, 4),
        htmlspecialchars($source),
        htmlspecialchars($destination),
        number_format($tx['fee'] ?? 0, 6),
        htmlspecialchars($block),
        htmlspecialchars($lt),
        $time
    );
}

function formatTronAddress(string $value): string
{
    if ($value === '') {
        return '-';
    }
    if ($value[0] === 'T') {
        return $value;
    }
    if (!preg_match('/^41[a-f0-9]{40}$/i', $value)) {
        return $value;
    }
    return tronHexToBase58($value);
}

function tronHexToBase58(string $hex): string
{
    $bin = hex2bin($hex);
    if ($bin === false) {
        return $hex;
    }
    $hash = hash('sha256', hash('sha256', $bin, true), true);
    $payload = $bin . substr($hash, 0, 4);
    return base58Encode($payload);
}

function base58Encode(string $input): string
{
    $alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
    $digits = [0];
    $inputLength = strlen($input);
    for ($idx = 0; $idx < $inputLength; $idx++) {
        $carry = ord($input[$idx]);
        for ($i = 0, $len = count($digits); $i < $len; $i++) {
            $carry += $digits[$i] << 8;
            $digits[$i] = $carry % 58;
            $carry = intdiv($carry, 58);
        }
        while ($carry > 0) {
            $digits[] = $carry % 58;
            $carry = intdiv($carry, 58);
        }
    }
    $result = '';
    for ($idx = 0; $idx < $inputLength && $input[$idx] === "\0"; $idx++) {
        $result .= '1';
    }
    for ($i = count($digits) - 1; $i >= 0; $i--) {
        $result .= $alphabet[$digits[$i]];
    }
    return $result;
}

// Price fetch functions
function fetchTrxUsd(array $config): ?float
{
    $data = httpRequest($config['TRON']['price_endpoint']);
    if (!$data || empty($data['tron']['usd'])) {
        return null;
    }
    return (float)$data['tron']['usd'];
}

function fetchTrxMarketSnapshot(array $config): ?array
{
    $ticker = httpRequest($config['TRON']['chart_ticker_url']);
    $klines = httpRequest($config['TRON']['chart_klines_url']);
    if (!$ticker) {
        logError($config, 'TRX ticker fetch failed');
        return null;
    }
    $close = (float)($ticker['lastPrice'] ?? 0);
    $percent = (float)($ticker['priceChangePercent'] ?? 0);

    $candles = [];
    $chartError = null;
    if ($klines && is_array($klines)) {
        foreach ($klines as $entry) {
            if (!isset($entry[1]) || !isset($entry[2]) || !isset($entry[3]) || !isset($entry[4])) {
                continue;
            }
            $candles[] = [
                'open' => (float)$entry[1],
                'high' => (float)$entry[2],
                'low' => (float)$entry[3],
                'close' => (float)$entry[4],
            ];
        }
    }
    if (count($candles) < 5) {
        $chartError = 'داده چارت بایننس در دسترس نیست';
        for ($i = 0; $i < 40; $i++) {
            $base = max($close, 0.0001);
            $candles[] = [
                'open' => $base,
                'high' => $base * 1.01,
                'low' => $base * 0.99,
                'close' => $base,
            ];
        }
    }

    // Get price in Toman from Bitpin
    $priceToman = null;
    $tickers = httpRequest('https://api.bitpin.org/api/v1/mkt/tickers/');
    if ($tickers && is_array($tickers)) {
        foreach ($tickers as $ticker) {
            if (isset($ticker['symbol']) && $ticker['symbol'] === 'TRX_IRT') {
                $priceToman = (float)($ticker['price'] ?? 0);
                break;
            }
        }
    }

    return [
        'price' => $close,
        'percent' => $percent,
        'price_toman' => $priceToman,
        'candles' => $candles,
        'chart_error' => $chartError,
    ];
}

function fetchTonPrice(array $config): ?array
{
    $data = httpRequest($config['PRICE']['ton_price_url']);
    if (!$data || empty($data['the-open-network']['usd'])) {
        logError($config, 'TON ticker fetch failed');
        return null;
    }
    $price = (float)$data['the-open-network']['usd'];
    $percent = (float)($data['the-open-network']['usd_24h_change'] ?? 0);
    
    $binanceKlines = httpRequest('https://api.binance.com/api/v3/klines?symbol=TONUSDT&interval=30m&limit=48');
    $candles = [];
    $chartError = null;
    
    if ($binanceKlines && is_array($binanceKlines)) {
        foreach ($binanceKlines as $entry) {
            if (!isset($entry[1]) || !isset($entry[2]) || !isset($entry[3]) || !isset($entry[4])) {
                continue;
            }
            $candles[] = [
                'open' => (float)$entry[1],
                'high' => (float)$entry[2],
                'low' => (float)$entry[3],
                'close' => (float)$entry[4],
            ];
        }
    }
    
    if (count($candles) < 5) {
        $chartError = 'داده چارت بایننس در دسترس نیست';
        for ($i = 0; $i < 40; $i++) {
            $base = max($price, 0.01);
            $candles[] = [
                'open' => $base,
                'high' => $base * 1.01,
                'low' => $base * 0.99,
                'close' => $base,
            ];
        }
    }
    
    // Get price in Toman from Bitpin
    $priceToman = null;
    $tickers = httpRequest('https://api.bitpin.org/api/v1/mkt/tickers/');
    if ($tickers && is_array($tickers)) {
        foreach ($tickers as $ticker) {
            if (isset($ticker['symbol']) && $ticker['symbol'] === 'TON_IRT') {
                $priceToman = (float)($ticker['price'] ?? 0);
                break;
            }
        }
    }

    return [
        'price' => $price,
        'percent' => $percent,
        'price_toman' => $priceToman,
        'source' => 'CoinGecko',
        'candles' => $candles,
        'chart_error' => $chartError,
    ];
}

function fetchUsdtToman(array $config): ?array
{
    // Fetch from Bitpin API
    $tickers = httpRequest('https://api.bitpin.org/api/v1/mkt/tickers/');
    
    if (!$tickers || !is_array($tickers)) {
        logError($config, 'USDT ticker fetch failed from Bitpin - invalid response');
        return null;
    }
    
    // Find USDT_IRT ticker
    $usdtTicker = null;
    foreach ($tickers as $ticker) {
        if (isset($ticker['symbol']) && $ticker['symbol'] === 'USDT_IRT') {
            $usdtTicker = $ticker;
            break;
        }
    }
    
    if (!$usdtTicker || empty($usdtTicker['price'])) {
        logError($config, 'USDT_IRT ticker not found in Bitpin response');
        return null;
    }
    
    $price = (float)$usdtTicker['price'];
    $change = (float)($usdtTicker['daily_change_price'] ?? 0);
    $low = (float)($usdtTicker['low'] ?? $price);
    $high = (float)($usdtTicker['high'] ?? $price);
    
    // Generate realistic 24-hour candles with balanced size
    $candles = [];
    $priceRange = $high - $low;
    if ($priceRange == 0) {
        $priceRange = abs($price * ($change / 100)); // Use change percentage if no range
        if ($priceRange == 0) {
            $priceRange = $price * 0.005; // 0.5% default range for stability
        }
    }
    
    // Calculate starting price (24 hours ago)
    $startPrice = $change < 0 ? $high : $low;
    $endPrice = $price; // Current price
    
    // Create 40 candles representing 24 hours (each candle ~36 minutes)
    for ($i = 0; $i < 40; $i++) {
        $progress = $i / 39; // 0 (24h ago) to 1 (now)
        
        // Smooth trend from start to end
        $basePrice = $startPrice + (($endPrice - $startPrice) * $progress);
        
        // Small, subtle variations (not big waves)
        $variation = sin($progress * M_PI * 4) * ($priceRange * 0.1); // Small wave
        $smallNoise = (rand(-50, 50) / 100) * ($priceRange * 0.01); // Tiny noise
        
        $candlePrice = $basePrice + $variation + $smallNoise;
        $candlePrice = max($low, min($high, $candlePrice));
        
        // Small, realistic body size (0.1% to 0.4% of price - very small for stable coin)
        $bodyPercent = 0.001 + (rand(0, 30) / 10000); // 0.1% to 0.4%
        $bodySize = $candlePrice * $bodyPercent;
        
        // Determine direction based on trend
        $prevPrice = $i > 0 ? $candles[$i - 1]['close'] : $startPrice;
        $isUp = $candlePrice > $prevPrice;
        
        // Set open and close
        if ($isUp) {
            $open = $candlePrice - ($bodySize / 2);
            $close = $candlePrice + ($bodySize / 2);
        } else {
            $open = $candlePrice + ($bodySize / 2);
            $close = $candlePrice - ($bodySize / 2);
        }
        
        // Small wicks (0.1% to 0.3% of price)
        $upperWick = $candlePrice * (0.001 + (rand(0, 20) / 10000)); // 0.1% to 0.3%
        $lowerWick = $candlePrice * (0.001 + (rand(0, 20) / 10000));
        
        $candleHigh = max($open, $close) + $upperWick;
        $candleLow = min($open, $close) - $lowerWick;
        
        // Clamp to daily bounds
        $candleHigh = min($candleHigh, $high);
        $candleLow = max($candleLow, $low);
        $open = max($candleLow, min($candleHigh, $open));
        $close = max($candleLow, min($candleHigh, $close));
        
        $candles[] = [
            'open' => $open,
            'high' => $candleHigh,
            'low' => $candleLow,
            'close' => $close,
        ];
    }
    
    return [
        'price' => $price,
        'change' => $change,
        'candles' => $candles,
    ];
}

// Chart generation functions
function downloadGoogleFont(string $fontFamily, string $weight = '400', string $style = 'normal'): ?string
{
    $fontsDir = __DIR__ . '/fonts';
    if (!is_dir($fontsDir)) {
        mkdir($fontsDir, 0775, true);
    }
    
    // Map font family to Google Fonts API name
    $fontMap = [
        'roboto' => 'Roboto',
        'poppins' => 'Poppins',
        'opensans' => 'Open Sans',
        'lato' => 'Lato',
        'montserrat' => 'Montserrat',
    ];
    
    $fontFamilyLower = strtolower($fontFamily);
    $googleFontName = $fontMap[$fontFamilyLower] ?? $fontFamily;
    
    // Create filename
    $weightName = $weight === '700' || $weight === 'bold' ? 'Bold' : 'Regular';
    $filename = strtolower(str_replace(' ', '', $googleFontName)) . ($weightName === 'Bold' ? 'Bold' : 'Regular') . '.ttf';
    $filepath = $fontsDir . '/' . $filename;
    
    // If font already exists, return it
    if (file_exists($filepath)) {
        return $filepath;
    }
    
    // Download from Google Fonts
    // Using fonts.google.com download link
    $fontUrl = sprintf(
        'https://fonts.google.com/download?family=%s',
        urlencode($googleFontName)
    );
    
    // Alternative: Use GitHub CDN for popular fonts
    $githubUrls = [
        'roboto' => [
            'regular' => 'https://github.com/google/fonts/raw/main/apache/roboto/Roboto-Regular.ttf',
            'bold' => 'https://github.com/google/fonts/raw/main/apache/roboto/Roboto-Bold.ttf',
        ],
        'poppins' => [
            'regular' => 'https://github.com/google/fonts/raw/main/ofl/poppins/Poppins-Regular.ttf',
            'bold' => 'https://github.com/google/fonts/raw/main/ofl/poppins/Poppins-Bold.ttf',
        ],
        'opensans' => [
            'regular' => 'https://github.com/google/fonts/raw/main/apache/opensans/OpenSans-Regular.ttf',
            'bold' => 'https://github.com/google/fonts/raw/main/apache/opensans/OpenSans-Bold.ttf',
        ],
        'lato' => [
            'regular' => 'https://github.com/google/fonts/raw/main/ofl/lato/Lato-Regular.ttf',
            'bold' => 'https://github.com/google/fonts/raw/main/ofl/lato/Lato-Bold.ttf',
        ],
        'montserrat' => [
            'regular' => 'https://github.com/google/fonts/raw/main/ofl/montserrat/Montserrat-Regular.ttf',
            'bold' => 'https://github.com/google/fonts/raw/main/ofl/montserrat/Montserrat-Bold.ttf',
        ],
    ];
    
    $fontKey = $fontFamilyLower;
    $weightKey = ($weightName === 'Bold') ? 'bold' : 'regular';
    
    if (isset($githubUrls[$fontKey][$weightKey])) {
        $fontData = @file_get_contents($githubUrls[$fontKey][$weightKey]);
        if ($fontData && strlen($fontData) > 1000) { // Valid font file should be > 1KB
            file_put_contents($filepath, $fontData);
            return $filepath;
        }
    }
    
    return null;
}

function getFontPath(string $fontName = 'roboto', bool $bold = false): ?string
{
    $fontsDir = __DIR__ . '/fonts';
    if (!is_dir($fontsDir)) {
        mkdir($fontsDir, 0775, true);
    }
    
    // Try to download Google Font if not exists
    $googleFont = downloadGoogleFont($fontName, $bold ? '700' : '400');
    if ($googleFont && file_exists($googleFont)) {
        return $googleFont;
    }
    
    // Try custom font in fonts directory
    $customFont = $fontsDir . '/' . strtolower($fontName) . ($bold ? 'bold' : 'regular') . '.ttf';
    if (file_exists($customFont)) {
        return $customFont;
    }
    
    // Try alternative naming
    $customFont2 = $fontsDir . '/' . $fontName . ($bold ? 'Bold' : 'Regular') . '.ttf';
    if (file_exists($customFont2)) {
        return $customFont2;
    }
    
    // Fallback to system fonts
    $paths = [];
    
    if (PHP_OS_FAMILY === 'Windows') {
        if ($bold) {
            $paths[] = 'C:\\Windows\\Fonts\\segoeuib.ttf';
            $paths[] = 'C:\\Windows\\Fonts\\calibrib.ttf';
            $paths[] = 'C:\\Windows\\Fonts\\arialbd.ttf';
        }
        $paths[] = 'C:\\Windows\\Fonts\\segoeui.ttf';
        $paths[] = 'C:\\Windows\\Fonts\\calibri.ttf';
        $paths[] = 'C:\\Windows\\Fonts\\arial.ttf';
    } elseif (PHP_OS_FAMILY === 'Linux') {
        if ($bold) {
            $paths[] = '/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf';
            $paths[] = '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf';
        }
        $paths[] = '/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf';
        $paths[] = '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf';
    }
    
    foreach ($paths as $path) {
        if (file_exists($path)) {
            return $path;
        }
    }
    
    return null;
}

function drawText($img, int $size, int $x, int $y, int $color, string $text, bool $bold = false, array $config = null): void
{
    if (!$img) {
        return;
    }
    
    // Try Google Fonts first (Roboto is a beautiful, modern font)
    $fontPath = getFontPath('roboto', $bold);
    
    // If Roboto not available, try Poppins
    if (!$fontPath || !file_exists($fontPath)) {
        $fontPath = getFontPath('poppins', $bold);
    }
    
    // If still not available, try Open Sans
    if (!$fontPath || !file_exists($fontPath)) {
        $fontPath = getFontPath('opensans', $bold);
    }
    
    // If we have a TTF font, use it
    if ($fontPath && file_exists($fontPath) && function_exists('imagettftext')) {
        try {
            $ttfSize = (int)$size;
            if ($ttfSize < 10) {
                $ttfSize = 10;
            }
            
            // Calculate text bounding box to adjust Y position
            $bbox = @imagettfbbox($ttfSize, 0, $fontPath, $text);
            if ($bbox !== false) {
                // TTF uses baseline, so adjust Y
                $textHeight = abs($bbox[7] - $bbox[1]);
                $adjustedY = $y + $textHeight;
                
                $result = @imagettftext($img, $ttfSize, 0, $x, $adjustedY, $color, $fontPath, $text);
                if ($result !== false) {
                    return;  // Success!
                }
            }
        } catch (Exception $e) {
            if ($config) {
                logError($config, 'TTF font error: ' . $e->getMessage() . ' - Font: ' . ($fontPath ?? 'null'));
            }
        } catch (Error $e) {
            if ($config) {
                logError($config, 'TTF font fatal error: ' . $e->getMessage() . ' - Font: ' . ($fontPath ?? 'null'));
            }
        }
    }
    
    // Last resort: use imagestring (very limited, no scaling)
    if ($config) {
        logError($config, 'Failed to use TTF font, falling back to imagestring. Font path: ' . ($fontPath ?? 'null'));
    }
    $font = $bold ? 5 : 3;
    @imagestring($img, $font, $x, $y - 15, $text, $color);
}

function generateAdvancedChartImage(string $backgroundImagePath, string $symbol, string $pair, float $price, float $percent, array $candles, array $config): ?string
{
    if (!extension_loaded('gd')) {
        logError($config, 'GD extension not loaded');
        return null;
    }
    
    if (!file_exists($backgroundImagePath)) {
        logError($config, 'Background image not found: ' . $backgroundImagePath);
        return null;
    }
    
    try {
        $img = @imagecreatefrompng($backgroundImagePath);
        if (!$img) {
            $error = error_get_last();
            logError($config, 'Failed to load background image: ' . $backgroundImagePath . ' - Error: ' . ($error['message'] ?? 'Unknown'));
            return null;
        }
        
        $width = imagesx($img);
        $height = imagesy($img);
        
        imagealphablending($img, true);
        imagesavealpha($img, true);
    
        $black = imagecolorallocate($img, 0, 0, 0);
        $green = imagecolorallocate($img, 34, 197, 94);
        $red = imagecolorallocate($img, 239, 68, 68);
        $gray = imagecolorallocate($img, 156, 163, 175);
        $white = imagecolorallocate($img, 255, 255, 255);
        
        // Only draw price and percent change (symbol and pair are already in the image)
        // Position at exact pixel coordinates as requested
        if (strpos($pair, 'IRT') !== false) {
            $priceText = number_format($price, 0);
        } else {
            $decimals = $price < 1 ? 4 : 3;
            $priceText = '$' . number_format($price, $decimals);
        }
        // Position price at exact coordinates: 242, 362
        // Use larger font sizes with more spacing
        $priceX = 200;
        $priceY = 362;
        drawText($img, 75, $priceX, $priceY, $black, $priceText, true, $config);  // Larger font for price
        
        $percentText = ($percent >= 0 ? '+' : '') . number_format($percent, 2) . '%';
        $percentColor = $percent >= 0 ? $green : $red;
        $percentX = 242;  // Same X as price (aligned)
        $percentY = 362 + 120;  // Below price (110px spacing for better separation)
        drawText($img, 55, $percentX, $percentY, $percentColor, $percentText, true, $config);  // Larger font for percent
        
        // Chart area - stretched horizontally as requested: from 132px to 1747px, Y=526px
        $chartX = 20;   // Start X position
        $chartY = 526;   // Start Y position
        $chartW = 1747 - 20;  // Width: from 132 to 1747 (1615px wide)
        $chartH = $height - 526 - 80;  // Height: from Y=526 to bottom with 50px margin
        
        drawCandlestickChart($img, $candles, $chartX, $chartY, $chartW, $chartH, $black, $green, $red, $gray);
    
        $storageDir = __DIR__ . '/storage/charts';
        if (!is_dir($storageDir)) {
            mkdir($storageDir, 0775, true);
        }
    
        $filename = strtolower($symbol) . '_' . time() . '.png';
        $filepath = $storageDir . '/' . $filename;
    
        if (@imagepng($img, $filepath, 9)) {
            imagedestroy($img);
            return $filepath;
        } else {
            logError($config, 'Failed to save image to: ' . $filepath);
        }
        
        imagedestroy($img);
        return null;
    } catch (Exception $e) {
        logError($config, 'Error in generateAdvancedChartImage: ' . $e->getMessage());
        if (isset($img) && is_resource($img)) {
            @imagedestroy($img);
        }
        return null;
    } catch (Error $e) {
        logError($config, 'Fatal error in generateAdvancedChartImage: ' . $e->getMessage());
        if (isset($img) && is_resource($img)) {
            @imagedestroy($img);
        }
        return null;
    }
}

function drawCandlestickChart($img, array $candles, $x, $y, $w, $h, $black, $green, $red, $gray)
{
    if (empty($candles)) {
        return;
    }
    
    // Get image dimensions to ensure we don't exceed bounds
    $imgWidth = imagesx($img);
    $imgHeight = imagesy($img);
    
    // Reduced padding to fit better within image - use percentage-based
    $imgWidth = imagesx($img);
    $imgHeight = imagesy($img);
    
    // Use smaller padding relative to chart area
    $padding_left = (int)round($w * 0.15);   // 15% of chart width
    $padding_right = (int)round($w * 0.05);  // 5% of chart width
    $padding_top = (int)round($h * 0.10);     // 10% of chart height
    $padding_bottom = (int)round($h * 0.15);  // 15% of chart height
    
    $chart_x = (int)round($x + $padding_left);
    $chart_y = (int)round($y + $padding_top);
    $chart_width = (int)round($w - $padding_left - $padding_right);
    $chart_height = (int)round($h - $padding_top - $padding_bottom);
    
    // Ensure chart doesn't exceed image bounds
    if ($chart_x + $chart_width > $imgWidth) {
        $chart_width = $imgWidth - $chart_x - 10;
    }
    if ($chart_y + $chart_height > $imgHeight) {
        $chart_height = $imgHeight - $chart_y - 10;
    }
    
    $border_color = $gray;
    $borderX1 = max(0, (int)round($chart_x - 5));
    $borderY1 = max(0, (int)round($chart_y - 5));
    $borderX2 = min($imgWidth - 1, (int)round($x + $w - $padding_right + 5));
    $borderY2 = min($imgHeight - 1, (int)round($y + $h - $padding_bottom + 5));
    
    imagerectangle($img, $borderX1, $borderY1, $borderX2, $borderY2, $border_color);
    
    $gridLines = 10;
    for ($i = 0; $i <= $gridLines; $i++) {
        $gridY = (int)round($chart_y + $i * ($chart_height / $gridLines));
        // Ensure grid line is within image bounds
        if ($gridY >= 0 && $gridY < $imgHeight) {
            $lineX2 = min($imgWidth - 1, (int)($chart_x + $chart_width));
            imageline($img, (int)$chart_x, $gridY, $lineX2, $gridY, $gray);
        }
    }
    
    $maxPrice = PHP_FLOAT_MIN;
    $minPrice = PHP_FLOAT_MAX;
    foreach ($candles as $candle) {
        $maxPrice = max($maxPrice, $candle['high']);
        $minPrice = min($minPrice, $candle['low']);
    }
    
    $pricePadding = ($maxPrice - $minPrice) * 0.05;
    $minPrice -= $pricePadding;
    $maxPrice += $pricePadding;
    $priceRange = $maxPrice - $minPrice;
    if ($priceRange == 0) {
        $priceRange = 1;
    }
    
    $decimalPlaces = ($maxPrice < 1) ? 6 : 3;
    
    for ($i = 0; $i <= $gridLines; $i++) {
        $price = $maxPrice - $i * ($priceRange / $gridLines);
        $labelY = (int)round($chart_y + $i * ($chart_height / $gridLines));
        $label = number_format($price, $decimalPlaces);
        // Ensure label position is within image bounds
        $labelX = max(5, (int)round($chart_x - 70));
        if ($labelY >= 0 && $labelY < $imgHeight) {
            drawText($img, 12, $labelX, $labelY + 5, $gray, $label);
        }
    }
    
    $barWidth = $chart_width / count($candles);
    
    foreach ($candles as $idx => $candle) {
        $open = (float)$candle['open'];
        $close = (float)$candle['close'];
        $high = (float)$candle['high'];
        $low = (float)$candle['low'];
        
        $candleX = (int)round($chart_x + $idx * $barWidth);
        $candleCenterX = (int)round($candleX + ($barWidth / 2));
        
        $yOpen = (int)round($chart_y + $chart_height - (($open - $minPrice) / $priceRange * $chart_height));
        $yClose = (int)round($chart_y + $chart_height - (($close - $minPrice) / $priceRange * $chart_height));
        $yHigh = (int)round($chart_y + $chart_height - (($high - $minPrice) / $priceRange * $chart_height));
        $yLow = (int)round($chart_y + $chart_height - (($low - $minPrice) / $priceRange * $chart_height));
        
        // Clamp Y values to image bounds
        $yHigh = max(0, min($imgHeight - 1, $yHigh));
        $yLow = max(0, min($imgHeight - 1, $yLow));
        $yOpen = max(0, min($imgHeight - 1, $yOpen));
        $yClose = max(0, min($imgHeight - 1, $yClose));
        
        // Ensure candle X position is within bounds
        $candleXRight = min($imgWidth - 1, (int)($candleX + $barWidth - 2));
        $candleXLeft = max(0, (int)($candleX + 2));
        
        if ($candleCenterX >= 0 && $candleCenterX < $imgWidth) {
            imageline($img, $candleCenterX, $yHigh, $candleCenterX, $yLow, $black);
        }
        
        if ($close >= $open) {
            imagefilledrectangle($img, 
                $candleXLeft, 
                $yOpen, 
                $candleXRight, 
                $yClose, 
                $green
            );
        } else {
            imagefilledrectangle($img, 
                $candleXLeft, 
                $yClose, 
                $candleXRight, 
                $yOpen, 
                $red
            );
        }
    }
}

function generateTrxCard(array $data, array $config): ?string
{
    // Try multiple possible paths for logo (__DIR__ in functions.php points to the same directory as index.php)
    $possiblePaths = [
        __DIR__ . '/logo/tron.png',  // Same directory as functions.php
        dirname(__FILE__) . '/logo/tron.png',  // Alternative
    ];
    $backgroundImagePath = null;
    foreach ($possiblePaths as $path) {
        $realPath = realpath($path);
        if ($realPath && file_exists($realPath)) {
            $backgroundImagePath = $realPath;
            break;
        }
    }
    if (!$backgroundImagePath) {
        // Fallback to first path
        $backgroundImagePath = __DIR__ . '/logo/tron.png';
    }
    $candles = $data['candles'] ?? [];
    
    if (empty($candles)) {
        $base = max($data['price'], 0.0001);
        for ($i = 0; $i < 40; $i++) {
            $candles[] = [
                'open' => $base * (1 + ($i * 0.001)),
                'high' => $base * (1.01 + ($i * 0.001)),
                'low' => $base * (0.99 + ($i * 0.001)),
                'close' => $base * (1 + (($i + 1) * 0.001)),
            ];
        }
    }
    
    $result = generateAdvancedChartImage(
        $backgroundImagePath,
        'TRX',
        'TRX / USD',
        $data['price'],
        $data['percent'],
        $candles,
        $config
    );
    
    if ($result && file_exists($result)) {
        return $result;
    }
    
    logError($config, 'Advanced chart generation failed for TRX. Result: ' . ($result ?? 'null'));
    return null;
}

function generateTonCard(array $data, array $config): ?string
{
    // Try multiple possible paths for logo (__DIR__ in functions.php points to the same directory as index.php)
    $possiblePaths = [
        __DIR__ . '/logo/ton.png',  // Same directory as functions.php
        dirname(__FILE__) . '/logo/ton.png',  // Alternative
    ];
    $backgroundImagePath = null;
    foreach ($possiblePaths as $path) {
        $realPath = realpath($path);
        if ($realPath && file_exists($realPath)) {
            $backgroundImagePath = $realPath;
            break;
        }
    }
    if (!$backgroundImagePath) {
        // Fallback to first path
        $backgroundImagePath = __DIR__ . '/logo/ton.png';
    }
    $candles = $data['candles'] ?? [];
    
    if (empty($candles)) {
        $base = max($data['price'], 0.01);
        for ($i = 0; $i < 40; $i++) {
            $candles[] = [
                'open' => $base * (1 + ($i * 0.001)),
                'high' => $base * (1.01 + ($i * 0.001)),
                'low' => $base * (0.99 + ($i * 0.001)),
                'close' => $base * (1 + (($i + 1) * 0.001)),
            ];
        }
    }
    
    $result = generateAdvancedChartImage(
        $backgroundImagePath,
        'TON',
        'TON / USD',
        $data['price'],
        $data['percent'],
        $candles,
        $config
    );
    
    if ($result && file_exists($result)) {
        return $result;
    }
    
    logError($config, 'Advanced chart generation failed for TON. Result: ' . ($result ?? 'null'));
    return null;
}

function generateUsdtCard(array $data, array $config): ?string
{
    // Try multiple possible paths for logo (__DIR__ in functions.php points to the same directory as index.php)
    $possiblePaths = [
        __DIR__ . '/logo/usdt.png',  // Same directory as functions.php
        dirname(__FILE__) . '/logo/usdt.png',  // Alternative
    ];
    $backgroundImagePath = null;
    foreach ($possiblePaths as $path) {
        $realPath = realpath($path);
        if ($realPath && file_exists($realPath)) {
            $backgroundImagePath = $realPath;
            break;
        }
    }
    if (!$backgroundImagePath) {
        // Fallback to first path
        $backgroundImagePath = __DIR__ . '/logo/usdt.png';
    }
    $candles = $data['candles'] ?? [];
    
    if (empty($candles)) {
        $basePrice = $data['price'];
        for ($i = 0; $i < 40; $i++) {
            $variation = 1 + (($data['change'] / 100) * (40 - $i) / 40) + (rand(-50, 50) / 10000);
            $candlePrice = $basePrice * $variation;
            $candles[] = [
                'open' => $candlePrice * 0.998,
                'high' => $candlePrice * 1.005,
                'low' => $candlePrice * 0.995,
                'close' => $candlePrice,
            ];
        }
    }
    
    return generateAdvancedChartImage(
        $backgroundImagePath,
        'USDT',
        'USDT / IRT',
        $data['price'],
        $data['change'],
        $candles,
        $config
    );
}

