<?php

namespace App\Libraries;

use CodeIgniter\Config\Services;

/**
 * Simplified wrapper for YEMChain / YEMScan API (https://yemscan.com/api/).
 * Auth via headers: Apikey, Customdomain, Pernum (Pernum = UID + 1000000000).
 * Rate limit: 10 requests per minute per API key.
 */
class YemscanService
{
    private $encrypter;
    private $apiBaseUrl;
    private $foundationApiUrl;
    private $customDomain;
    private $pernum;
    private $encryptedApiKey;
    private $timeoutSeconds;
    private $foundationTimeoutSeconds;
    private $foundationPlatformName;

    public function __construct()
    {
        try {
            $this->encrypter = Services::encrypter();
        } catch (\Exception $e) {
            log_message('error', 'Encryption service not available: ' . $e->getMessage());
            $this->encrypter = null;
        }

        $this->apiBaseUrl = rtrim($_ENV['yemscan.api_base_url'] ?? 'https://yemscan.com/api', '/');
        $this->foundationApiUrl = rtrim($_ENV['yemscan.foundation_api_url'] ?? 'https://yem.foundation/api', '/');
        $this->customDomain = trim($_ENV['yemscan.custom_domain'] ?? '');
        $this->timeoutSeconds = (int) ($_ENV['yemscan.timeout_seconds'] ?? 30);
        $this->foundationTimeoutSeconds = (int) ($_ENV['yemscan.foundation_timeout_seconds'] ?? 30);
        $this->foundationPlatformName = trim($_ENV['yemscan.foundation_platform_name'] ?? ($_ENV['yemscan.platform_name'] ?? 'FinFlo'));

        // Pernum: use explicit pernum if set (e.g. testnet 1001296648), else uid + 1000000000
        $pernumEnv = trim($_ENV['yemscan.pernum'] ?? '');
        if ($pernumEnv !== '') {
            $this->pernum = $pernumEnv;
        } else {
            $uid = $_ENV['yemscan.uid'] ?? '';
            $uidInt = (int) (is_string($uid) ? preg_replace('/[^0-9-]/', '', $uid) : $uid);
            $this->pernum = $uidInt !== 0 ? (string) ($uidInt + self::PERNUM_OFFSET) : '';
        }

        $apiKey = $_ENV['yemscan.api_key'] ?? '';
        if ($this->encrypter && !empty($apiKey)) {
            $this->encryptedApiKey = $this->encrypter->encrypt($apiKey);
        } else {
            $this->encryptedApiKey = $apiKey !== '' ? base64_encode($apiKey) : '';
        }
    }

    private function getDecryptedApiKey(): string
    {
        if (empty($this->encryptedApiKey)) {
            return '';
        }
        if ($this->encrypter) {
            return $this->encrypter->decrypt($this->encryptedApiKey);
        }
        return base64_decode($this->encryptedApiKey);
    }

    private function getHeaders(): array
    {
        return $this->getHeadersWithPernum($this->pernum);
    }

    /**
     * Headers for a specific user (Pernum = uid + 1000000000).
     * Use when YEMScan requires the requesting user's Pernum (access-granted flow).
     */
    private function getHeadersForUser(int $uid): array
    {
        $pernum = (string) ($uid + self::PERNUM_OFFSET);
        return $this->getHeadersWithPernum($pernum);
    }

    private function getHeadersWithPernum(string $pernum): array
    {
        $apiKey = $this->getDecryptedApiKey();
        $headers = [
            'Content-Type' => 'application/json',
            'Accept'       => 'application/json',
        ];
        if ($apiKey !== '') {
            $headers['Apikey'] = $apiKey;
        }
        if ($this->customDomain !== '') {
            $headers['Customdomain'] = $this->customDomain;
        }
        if ($pernum !== '') {
            $headers['Pernum'] = $pernum;
        }
        return $headers;
    }

    /**
     * Headers for YEM Foundation API (e.g. validate-access / get_third_party_access).
     * Uses dedicated Foundation env vars when present:
     * - yemscan.foundation_api_key
     * - yemscan.foundation_custom_domain
     * - yemscan.foundation_pernum (or yemscan.foundation_uid -> pernum = uid + 1000000000)
     *
     * Falls back to the normal yemscan.* credentials if foundation_* are not set.
     */
    private function getHeadersForFoundation(): array
    {
        $apiKey = trim($_ENV['yemscan.foundation_api_key'] ?? '');
        if ($apiKey === '') {
            $apiKey = $this->getDecryptedApiKey();
        }

        $customDomain = trim($_ENV['yemscan.foundation_custom_domain'] ?? '');
        if ($customDomain === '') {
            // Keep your current behavior as safe default
            $customDomain = 'finflo.online';
        }

        $pernum = trim($_ENV['yemscan.foundation_pernum'] ?? '');
        if ($pernum === '') {
            $uid = $_ENV['yemscan.foundation_uid'] ?? '';
            $uidInt = (int) (is_string($uid) ? preg_replace('/[^0-9-]/', '', $uid) : $uid);
            if ($uidInt !== 0) {
                $pernum = (string) ($uidInt + self::PERNUM_OFFSET);
            } else {
                // fallback to existing yemscan.pernum
                $pernum = $this->pernum;
            }
        }

        $headers = [
            'Content-Type' => 'application/json',
            'Accept'       => 'application/json',
        ];
        if ($apiKey !== '') {
            $headers['Apikey'] = $apiKey;
        }
        if ($customDomain !== '') {
            $headers['Customdomain'] = $customDomain;
        }
        if ($pernum !== '') {
            $headers['Pernum'] = $pernum;
        }
        return $headers;
    }

    /** Pernum = UID + this constant (YEM API convention). */
    private const PERNUM_OFFSET = 1000000000;

    /**
     * Get UID from pernum: uid = pernum - 1000000000.
     * If $pernum is null, uses the configured yemscan.pernum and returns this app's UID.
     *
     * @param int|string|null $pernum Pernum (e.g. 1001296648) or null to use configured pernum
     * @return array<string, mixed> success, uid, pernum on success; success false and message on failure
     */
    public function getUidFromPernum($pernum = null): array
    {
        if ($pernum === null || $pernum === '') {
            $pernum = $this->pernum;
        }
        $pernumInt = (int) (is_string($pernum) ? preg_replace('/[^0-9-]/', '', $pernum) : $pernum);
        if ($pernumInt <= 0) {
            return [
                'success' => false,
                'message' => 'Invalid or missing pernum',
            ];
        }
        $uid = $pernumInt - self::PERNUM_OFFSET;
        return [
            'success' => true,
            'uid'     => $uid,
            'pernum'  => $pernumInt,
        ];
    }

    /**
     * Validate that a UID is authorized to access this app (FinFlo) via YEM Foundation.
     * Calls https://yem.foundation/api/get_third_party_access.php with foundation-specific headers
     * (Apikey + Pernum from env, Customdomain = finflo.online). Does not use YEMScan headers.
     *
     * @param int         $uid          User UID (Pernum = uid + 1000000000 is not sent for the user; we use platform credentials)
     * @param string|null $platformName Platform name for the check (default FinFlo)
     * @return array{success: bool, access_granted?: bool, uid?: int, platform_name?: string, message?: string}
     */
    public function validateThirdPartyAccess(int $uid, ?string $platformName = null): array
    {
        $platformName = $platformName !== null && $platformName !== ''
            ? trim($platformName)
            : ($this->foundationPlatformName !== '' ? $this->foundationPlatformName : 'FinFlo');
        try {
            // Cache to reduce repeated Foundation calls (important for app open / polling)
            $cacheTtl = (int) ($_ENV['yemscan.foundation_access_cache_ttl_seconds'] ?? 300);
            $cacheKey = 'yemscan_foundation_access_' . $uid . '_' . md5($platformName);
            if ($cacheTtl > 0) {
                $cache = \Config\Services::cache();
                $cached = $cache->get($cacheKey);
                if (is_array($cached)) {
                    return $cached;
                }
            }

            $url = $this->foundationApiUrl . '/get_third_party_access.php?uid=' . (int) $uid
                . '&platform_name=' . rawurlencode($platformName);
            $client = Services::curlrequest();
            $response = $client->get($url, [
                'headers' => $this->getHeadersForFoundation(),
                'timeout' => max(1, $this->foundationTimeoutSeconds),
                'verify'  => ($_ENV['yemscan.verify_ssl'] ?? 'true') === 'true',
                'http_errors' => false,
            ]);
            $body = $response->getBody();
            $code = $response->getStatusCode();
            $data = json_decode($body, true);
            if (json_last_error() !== JSON_ERROR_NONE || !is_array($data)) {
                return [
                    'success' => false,
                    'access_granted' => false,
                    'message' => $code >= 400 ? 'Foundation API error (HTTP ' . $code . ')' : 'Invalid JSON from Foundation API',
                ];
            }
            $success = isset($data['status']) && $data['status'] === 'success' && !empty($data['access_granted']);
            $result = [
                'success' => $success,
                'access_granted' => (bool) ($data['access_granted'] ?? false),
                'uid' => isset($data['uid']) ? (int) $data['uid'] : $uid,
                'platform_name' => $data['platform_name'] ?? $platformName,
                'message' => $success ? 'Access granted' : ($data['message'] ?? 'Access not granted'),
            ];
            if (!empty($cacheTtl) && isset($cache)) {
                $cache->save($cacheKey, $result, $cacheTtl);
            }
            return $result;
        } catch (\Throwable $e) {
            log_message('error', 'YEM Foundation validateThirdPartyAccess: ' . $e->getMessage());
            return [
                'success' => false,
                'access_granted' => false,
                'message' => 'Foundation API request failed: ' . $e->getMessage(),
            ];
        }
    }

    /**
     * Ensure hash has 0x prefix for API and verify flows.
     */
    private function normalizeHash(string $hash): string
    {
        $hash = trim($hash);
        if ($hash === '') {
            return $hash;
        }
        return strpos($hash, '0x') === 0 ? $hash : '0x' . $hash;
    }

    /**
     * Convert decimal YEM amount to API integer (hundredths: 10 YEM -> 1000, 12.34 YEM -> 1234).
     * The YEM API uses hundredths; value 10 on-chain = 0.10 YEM. Always treat input as decimal YEM.
     */
    private function valueToInteger($value): int
    {
        return (int) round((float) $value * 100);
    }

    /**
     * Convert API integer back to decimal (1234 -> 12.34).
     */
    private function integerToValue(int $value): float
    {
        return round($value / 100, 2);
    }

    /**
     * Normalize transaction from API response for verification (value as decimal, from_uid, to_uid).
     */
    private function normalizeTransactionForVerify(array $tx): array
    {
        $value = $tx['value'] ?? $tx['amount'] ?? 0;
        $valueInt = is_numeric($value) ? (int) $value : 0;
        $from = $tx['from_uid'] ?? $tx['from'] ?? null;
        $to = $tx['to_uid'] ?? $tx['to'] ?? null;
        return [
            'value_int' => $valueInt,
            'value' => $this->integerToValue($valueInt),
            'from_uid' => $from !== null ? (int) $from : null,
            'to_uid' => $to !== null ? (int) $to : null,
        ];
    }

    /**
     * Create transactions (batch, max 100).
     * Each item: from_uid, to_uid, value (decimal or integer), reason, asset, from_curr, to_curr.
     * When $userUid is set, requests use that user's Pernum (required for access-granted flow).
     */
    public function createTransactions(array $transactions, ?int $userUid = null): array
    {
        try {
            if (empty($transactions)) {
                return ['success' => false, 'message' => 'No transactions provided'];
            }
            if (count($transactions) > 100) {
                return ['success' => false, 'message' => 'Maximum 100 transactions per request'];
            }

            $required = ['from_uid', 'to_uid', 'value', 'reason', 'asset', 'from_curr', 'to_curr'];
            $payload = [];
            foreach ($transactions as $i => $tx) {
                foreach ($required as $field) {
                    if (!array_key_exists($field, $tx)) {
                        return ['success' => false, 'message' => "Transaction " . ($i + 1) . ": missing required field {$field}"];
                    }
                }
                $payload[] = [
                    'from_uid'  => (int) $tx['from_uid'],
                    'to_uid'    => (int) $tx['to_uid'],
                    'value'     => $this->valueToInteger($tx['value']),
                    'reason'    => (string) $tx['reason'],
                    'asset'     => (string) $tx['asset'],
                    'from_curr' => (string) $tx['from_curr'],
                    'to_curr'   => (string) $tx['to_curr'],
                ];
            }

            $headers = $this->getHeaders();
            $client = Services::curlrequest();
            $response = $client->post($this->apiBaseUrl . '/createTransactions.php', [
                'json' => $payload,
                'headers' => $headers,
                'timeout' => max(1, $this->timeoutSeconds),
                'verify' => ($_ENV['yemscan.verify_ssl'] ?? 'true') === 'true',
                'http_errors' => false,
            ]);

            $data = json_decode($response->getBody(), true);
            $code = $response->getStatusCode();

            if ($code === 200 && !empty($data['success'])) {
                $hashes = $data['transaction_hashes'] ?? [];
                $hashes = array_map(function ($h) {
                    return $this->normalizeHash((string) $h);
                }, $hashes);
                return [
                    'success' => true,
                    'message' => $data['message'] ?? 'Transactions created',
                    'inserted_count' => $data['inserted_count'] ?? count($payload),
                    'transaction_hashes' => array_values($hashes),
                ];
            }
            return [
                'success' => false,
                'message' => $data['message'] ?? $data['error'] ?? 'Create transactions failed',
                'error_code' => $code,
                'raw_response' => $data,
            ];
        } catch (\Exception $e) {
            log_message('error', 'Yemscan createTransactions: ' . $e->getMessage());
            return ['success' => false, 'message' => 'Internal server error', 'error' => $e->getMessage()];
        }
    }

    /**
     * Get balances for UIDs (max 100). tokenSymbol e.g. YEM.
     * When $userUid is set, requests use that user's Pernum (required for access-granted flow).
     */
    public function getBalances(array $uids, string $tokenSymbol = 'YEM', ?int $userUid = null): array
    {
        try {
            if (empty($uids)) {
                return ['success' => false, 'message' => 'uids required'];
            }
            if (count($uids) > 100) {
                return ['success' => false, 'message' => 'Maximum 100 UIDs per request'];
            }
            $headers = $this->getHeaders();
            $uidsStr = implode(',', array_map('intval', $uids));
            $url = $this->apiBaseUrl . '/getBalances.php?uids=' . rawurlencode($uidsStr) . '&tokenSymbol=' . rawurlencode($tokenSymbol);

            $client = Services::curlrequest();
            $response = $client->get($url, [
                'headers' => $headers,
                'timeout' => max(1, $this->timeoutSeconds),
                'verify' => ($_ENV['yemscan.verify_ssl'] ?? 'true') === 'true',
                'http_errors' => false,
            ]);

            $data = json_decode($response->getBody(), true);
            $code = $response->getStatusCode();

            if ($code === 200 && isset($data['balances'])) {
                return ['success' => true, 'message' => 'Balances retrieved', 'balances' => $data['balances']];
            }
            return [
                'success' => false,
                'message' => $data['error'] ?? $data['message'] ?? 'Get balances failed',
                'error_code' => $code,
                'raw_response' => $data,
            ];
        } catch (\Exception $e) {
            log_message('error', 'Yemscan getBalances: ' . $e->getMessage());
            return ['success' => false, 'message' => 'Internal server error', 'error' => $e->getMessage()];
        }
    }

    /**
     * Get global blockchain statistics.
     * When $userUid is set, requests use that user's Pernum (required for access-granted flow).
     */
    public function getGlobals(?int $userUid = null): array
    {
        try {
            $headers = $this->getHeaders();
            $client = Services::curlrequest();
            $response = $client->get($this->apiBaseUrl . '/getGlobals.php', [
                'headers' => $headers,
                'timeout' => max(1, $this->timeoutSeconds),
                'verify' => ($_ENV['yemscan.verify_ssl'] ?? 'true') === 'true',
                'http_errors' => false,
            ]);

            $data = json_decode($response->getBody(), true);
            $code = $response->getStatusCode();

            if ($code === 200 && !empty($data['success'])) {
                return [
                    'success' => true,
                    'message' => $data['message'] ?? 'Globals retrieved',
                    'total_accs' => $data['total_accs'] ?? null,
                    'total_txns' => $data['total_txns'] ?? null,
                ];
            }
            return [
                'success' => false,
                'message' => $data['message'] ?? $data['error'] ?? 'Get globals failed',
                'error_code' => $code,
                'raw_response' => $data,
            ];
        } catch (\Exception $e) {
            log_message('error', 'Yemscan getGlobals: ' . $e->getMessage());
            return ['success' => false, 'message' => 'Internal server error', 'error' => $e->getMessage()];
        }
    }

    /**
     * Get transaction volume (tokenSymbol e.g. YEM).
     * When $userUid is set, requests use that user's Pernum (required for access-granted flow).
     */
    public function getTransactionVolume(string $tokenSymbol = 'YEM', ?int $userUid = null): array
    {
        try {
            $headers = $this->getHeaders();
            $url = $this->apiBaseUrl . '/getTxnVolume.php?tokenSymbol=' . rawurlencode($tokenSymbol);
            $client = Services::curlrequest();
            $response = $client->get($url, [
                'headers' => $headers,
                'timeout' => max(1, $this->timeoutSeconds),
                'verify' => ($_ENV['yemscan.verify_ssl'] ?? 'true') === 'true',
                'http_errors' => false,
            ]);

            $data = json_decode($response->getBody(), true);
            $code = $response->getStatusCode();

            if ($code === 200 && isset($data['success']) && $data['success']) {
                return [
                    'success' => true,
                    'message' => $data['message'] ?? 'Volume retrieved',
                    'volume_1d' => $data['volume_1d'] ?? null,
                    'volume_7d' => $data['volume_7d'] ?? null,
                ];
            }
            return [
                'success' => false,
                'message' => $data['message'] ?? $data['error'] ?? 'Get volume failed',
                'error_code' => $code,
                'raw_response' => $data,
            ];
        } catch (\Exception $e) {
            log_message('error', 'Yemscan getTransactionVolume: ' . $e->getMessage());
            return ['success' => false, 'message' => 'Internal server error', 'error' => $e->getMessage()];
        }
    }

    /** Status: 0 not_found, 1 failed, 2 confirmed */
    private const STATUS_MAP = [0 => 'not_found', 1 => 'failed', 2 => 'confirmed'];

    /**
     * Get statuses for transaction hashes (max 100).
     * When $userUid is set, requests use that user's Pernum (required for access-granted flow).
     */
    public function getTransactionStatuses(array $hashes, bool $includeDetails = false, ?int $userUid = null): array
    {
        try {
            if (empty($hashes)) {
                return ['success' => false, 'message' => 'hashes required'];
            }
            if (count($hashes) > 100) {
                return ['success' => false, 'message' => 'Maximum 100 hashes per request'];
            }
            $headers = $this->getHeaders();
            $hashesStr = implode(',', array_map(function ($h) {
                return $this->normalizeHash(trim((string) $h));
            }, $hashes));
            $url = $this->apiBaseUrl . '/getTxnHashes.php?hashes=' . rawurlencode($hashesStr);

            $client = Services::curlrequest();
            $response = $client->get($url, [
                'headers' => $headers,
                'timeout' => max(1, $this->timeoutSeconds),
                'verify' => ($_ENV['yemscan.verify_ssl'] ?? 'true') === 'true',
                'http_errors' => false,
            ]);

            $data = json_decode($response->getBody(), true);
            $code = $response->getStatusCode();

            if ($code === 200 && isset($data['statuses'])) {
                $statuses = [];
                foreach ($data['statuses'] as $hash => $intStatus) {
                    $statuses[$this->normalizeHash((string) $hash)] = self::STATUS_MAP[(int) $intStatus] ?? 'unknown';
                }
                $out = ['success' => true, 'message' => 'Statuses retrieved', 'statuses' => $statuses];

                if ($includeDetails) {
                    $details = [];
                    foreach ($statuses as $hash => $status) {
                        if ($status === 'confirmed') {
                            $txResult = $this->getTransaction($this->normalizeHash($hash));
                            if ($txResult['success'] && isset($txResult['transaction'])) {
                                $details[$hash] = $this->normalizeTransactionForVerify($txResult['transaction']);
                            }
                        }
                    }
                    $out['details'] = $details;
                }
                return $out;
            }
            return [
                'success' => false,
                'message' => $data['error'] ?? 'Get statuses failed',
                'error_code' => $code,
                'raw_response' => $data,
            ];
        } catch (\Exception $e) {
            log_message('error', 'Yemscan getTransactionStatuses: ' . $e->getMessage());
            return ['success' => false, 'message' => 'Internal server error', 'error' => $e->getMessage()];
        }
    }

    /**
     * Verify a transaction: must be confirmed, and optionally match expected amount and recipient.
     * Use this to avoid accepting "confirmed" without checking amount/to_uid (prevents underpayment or wrong recipient).
     *
     * @param string $hash Transaction hash
     * @param float|null $expectedAmount Expected amount (decimal, e.g. 100.00 YEM). If null, amount is not checked.
     * @param int|null $expectedToUid Your merchant/recipient UID. If null, recipient is not checked.
     * @return array success, message, verified (bool), transaction (normalized: value, from_uid, to_uid), mismatch_reason
     */
    public function verifyTransaction(string $hash, ?float $expectedAmount = null, ?int $expectedToUid = null, ?int $userUid = null): array
    {
        $hash = $this->normalizeHash($hash);
        $statusResult = $this->getTransactionStatuses([$hash]);
        if (!$statusResult['success']) {
            return [
                'success' => false,
                'message' => $statusResult['message'],
                'verified' => false,
                'hash' => $hash,
            ];
        }
        $status = $statusResult['statuses'][$hash] ?? 'not_found';
        if ($status !== 'confirmed') {
            return [
                'success' => true,
                'message' => 'Transaction not confirmed',
                'verified' => false,
                'status' => $status,
                'hash' => $hash,
            ];
        }

        $txResult = $this->getTransaction($hash);
        if (!$txResult['success'] || !isset($txResult['transaction'])) {
            return [
                'success' => false,
                'message' => 'Could not fetch transaction details',
                'verified' => false,
                'hash' => $hash,
            ];
        }

        $norm = $this->normalizeTransactionForVerify($txResult['transaction']);

        // Response transaction: only decimal YEM amount (value / amount_yem), from_uid, to_uid.
        // Do not expose value_int (chain hundredths) to avoid consumers comparing expected_amount to it.
        $txForResponse = [
            'value'      => $norm['value'],
            'amount_yem' => $norm['value'],
            'from_uid'   => $norm['from_uid'],
            'to_uid'     => $norm['to_uid'],
        ];

        if ($expectedAmount !== null) {
            $diff = abs($norm['value'] - (float) $expectedAmount);
            if ($diff > 0.01) {
                return [
                    'success' => true,
                    'message' => 'Amount mismatch',
                    'verified' => false,
                    'hash' => $hash,
                    'transaction' => $txForResponse,
                    'expected_amount' => (float) $expectedAmount,
                    'mismatch_reason' => 'amount',
                ];
            }
        }

        if ($expectedToUid !== null && $norm['to_uid'] !== null && (int) $norm['to_uid'] !== (int) $expectedToUid) {
            return [
                'success' => true,
                'message' => 'Recipient mismatch',
                'verified' => false,
                'hash' => $hash,
                'transaction' => $txForResponse,
                'expected_to_uid' => (int) $expectedToUid,
                'mismatch_reason' => 'recipient',
            ];
        }

        return [
            'success' => true,
            'message' => 'Transaction verified',
            'verified' => true,
            'hash' => $hash,
            'transaction' => $txForResponse,
            'status' => 'confirmed',
        ];
    }

    /**
     * Get asset stats (price, wallets).
     * When $userUid is set, requests use that user's Pernum (required for access-granted flow).
     */
    public function getAssetStats(string $tokenSymbol = 'YEM', ?int $userUid = null): array
    {
        try {
            $headers = $this->getHeaders();
            $url = $this->apiBaseUrl . '/getAssetStats.php?tokenSymbol=' . rawurlencode($tokenSymbol);
            $client = Services::curlrequest();
            $response = $client->get($url, [
                'headers' => $headers,
                'timeout' => max(1, $this->timeoutSeconds),
                'verify' => ($_ENV['yemscan.verify_ssl'] ?? 'true') === 'true',
                'http_errors' => false,
            ]);

            $data = json_decode($response->getBody(), true);
            $code = $response->getStatusCode();

            if ($code === 200 && isset($data['price'])) {
                return [
                    'success' => true,
                    'message' => 'Asset stats retrieved',
                    'price' => $data['price'] ?? null,
                    'wallets' => $data['wallets'] ?? null,
                ];
            }
            return [
                'success' => false,
                'message' => $data['error'] ?? $data['message'] ?? 'Get asset stats failed',
                'error_code' => $code,
                'raw_response' => $data,
            ];
        } catch (\Exception $e) {
            log_message('error', 'Yemscan getAssetStats: ' . $e->getMessage());
            return ['success' => false, 'message' => 'Internal server error', 'error' => $e->getMessage()];
        }
    }

    /**
     * Get Ethereum public key for a UID.
     * When $requestingUserUid is set, requests use that user's Pernum (required for access-granted flow).
     */
    public function getPublicKey(int $uid, ?int $requestingUserUid = null): array
    {
        try {
            $headers = $this->getHeaders();
            $url = $this->apiBaseUrl . '/getPublicKey.php?uid=' . (int) $uid;
            $client = Services::curlrequest();
            $response = $client->get($url, [
                'headers' => $headers,
                'timeout' => max(1, $this->timeoutSeconds),
                'verify' => ($_ENV['yemscan.verify_ssl'] ?? 'true') === 'true',
                'http_errors' => false,
            ]);

            $data = json_decode($response->getBody(), true);
            $code = $response->getStatusCode();

            if ($code === 200 && !empty($data['success']) && isset($data['public_key'])) {
                return [
                    'success' => true,
                    'message' => 'Public key retrieved',
                    'public_key' => $data['public_key'],
                ];
            }
            return [
                'success' => false,
                'message' => $data['message'] ?? $data['error'] ?? 'Public key not found',
                'error_code' => $code,
                'raw_response' => $data,
            ];
        } catch (\Exception $e) {
            log_message('error', 'Yemscan getPublicKey: ' . $e->getMessage());
            return ['success' => false, 'message' => 'Internal server error', 'error' => $e->getMessage()];
        }
    }

    /**
     * Get transaction details by hash.
     * User access is enforced by the controller; YEMScan is called with platform headers.
     */
    public function getTransaction(string $hash, ?int $userUid = null): array
    {
        try {
            $hash = $this->normalizeHash($hash);
            if ($hash === '') {
                return ['success' => false, 'message' => 'hash required'];
            }
            // YEMScan may expect platform Pernum; user access is enforced by our API before calling.
            $headers = $this->getHeaders();
            $url = $this->apiBaseUrl . '/getTransactionHash.php?hash=' . rawurlencode($hash);
            $client = Services::curlrequest();
            $response = $client->get($url, [
                'headers' => $headers,
                'timeout' => max(1, $this->timeoutSeconds),
                'verify' => ($_ENV['yemscan.verify_ssl'] ?? 'true') === 'true',
                'http_errors' => false,
            ]);

            $data = json_decode($response->getBody(), true);
            $code = $response->getStatusCode();

            if ($code === 200 && $data !== null && isset($data['hash'])) {
                return ['success' => true, 'message' => 'Transaction retrieved', 'transaction' => $data];
            }
            $message = $data['message'] ?? $data['error'] ?? null;
            if ($code === 403) {
                $message = $message ?: 'YEMScan returned 403 Forbidden. Request may be disallowed for this user or domain.';
            }
            if ($message === null) {
                $message = 'Transaction not found';
            }
            return [
                'success' => false,
                'message' => $message,
                'error_code' => $code,
                'raw_response' => $data,
            ];
        } catch (\Exception $e) {
            log_message('error', 'Yemscan getTransaction: ' . $e->getMessage());
            return ['success' => false, 'message' => 'Internal server error', 'error' => $e->getMessage()];
        }
    }

    /**
     * Check if service is configured (for health endpoint).
     */
    public function isConfigured(): bool
    {
        $hasPernum = $this->pernum !== '';
        $hasUid = isset($_ENV['yemscan.uid']) && trim((string) $_ENV['yemscan.uid']) !== '';
        return $this->getDecryptedApiKey() !== ''
            && $this->customDomain !== ''
            && ($hasPernum || $hasUid);
    }
}
