<?php
namespace Bot;

use Dotenv\Dotenv;

/**
 * Configuration loader for the Telegram bot.
 *
 * This class reads environment variables from a .env file located in the
 * project root and exposes the resulting configuration array. The pattern
 * follows a simple singleton to avoid reloading configuration on each call.
 */
class Config
{
    /**
     * Cached instance of the configuration.
     *
     * @var self|null
     */
    private static ?self $instance = null;

    /**
     * Flattened configuration values loaded from the environment.
     *
     * @var array<string,mixed>
     */
    public array $data;

    /**
     * Constructor is private to enforce singleton usage.
     */
    private function __construct()
    {
        // Load environment variables from project root if present. The dotenv
        // library gracefully ignores missing files via safeLoad().
        $dotenv = Dotenv::createImmutable(__DIR__ . '/../');
        $dotenv->safeLoad();

        $alchemyKey = $_ENV['ALCHEMY_API_KEY'] ?? '';
        $alchemyNetwork = $_ENV['ALCHEMY_NETWORK'] ?? 'eth-mainnet';
        $alchemyUrl = $_ENV['ALCHEMY_API_URL']
            ?? ($alchemyKey ? sprintf('https://%s.g.alchemy.com/v2/%s', $alchemyNetwork, $alchemyKey) : '');
        $watchedTokens = $this->buildTokenConfig($_ENV['WATCH_TOKENS'] ?? 'USDT');

        // Build configuration array with sensible defaults. Sensitive
        // information should be provided via environment variables.
        $this->data = [
            'telegram_token' => $_ENV['TELEGRAM_BOT_TOKEN'] ?? '',
            'bot_username'   => $_ENV['TELEGRAM_BOT_USERNAME'] ?? '',
            'admin_ids'      => array_filter(array_map('trim', explode(',', $_ENV['TELEGRAM_ADMIN_IDS'] ?? ''))),
            'alchemy_api_key'  => $alchemyKey,
            'alchemy_network'  => $alchemyNetwork,
            'alchemy_api_url'  => $alchemyUrl,
            'trongrid_api_key' => $_ENV['TRONGRID_API_KEY'] ?? '',
            'trongrid_api_base'=> $_ENV['TRONGRID_API_BASE'] ?? 'https://api.trongrid.io',
            'default_network' => strtoupper($_ENV['DEFAULT_NETWORK'] ?? 'ERC20'),
            'supported_networks' => ['ERC20', 'BEP20', 'TRC20'],
            'supported_currencies' => array_filter(array_map('trim', explode(',', $_ENV['SUPPORTED_CURRENCIES'] ?? 'USDT,USDC,DAI'))),
            'wallet_encryption_key' => $_ENV['WALLET_ENC_KEY_BASE64'] ?? '',
            'kyc_required_threshold' => (float) ($_ENV['KYC_REQUIRED_THRESHOLD'] ?? 1000),
            'referral_bonus_amount' => (float) ($_ENV['REFERRAL_BONUS_AMOUNT'] ?? 0),
            'state_timeout_minutes' => (int) ($_ENV['STATE_TIMEOUT_MINUTES'] ?? 10),
            'payment_timeout_minutes' => (int) ($_ENV['PAYMENT_TIMEOUT_MINUTES'] ?? 15),
            'broadcast_batch_size' => (int) ($_ENV['BROADCAST_BATCH_SIZE'] ?? 500),
            'watch_lookback_blocks' => (int) ($_ENV['WATCHER_LOOKBACK_BLOCKS'] ?? 2000),
            'evm_chain_id' => (int) ($_ENV['EVM_CHAIN_ID'] ?? 1),
            'evm_gas_limit' => (int) ($_ENV['EVM_GAS_LIMIT'] ?? 120000),
            'evm_gas_price_gwei' => (int) ($_ENV['EVM_GAS_PRICE_GWEI'] ?? 5),
            'privacy_url' => $_ENV['PRIVACY_URL'] ?? '',
            'tos_url'     => $_ENV['TOS_URL'] ?? '',
            'referral_defaults' => [
                'point_rate'  => (float) ($_ENV['REFERRAL_POINT_RATE'] ?? 0.01),
                'kyc_points'  => (float) ($_ENV['REFERRAL_KYC_POINTS'] ?? 50),
                'trade_points'=> (float) ($_ENV['REFERRAL_TRADE_POINTS'] ?? 20),
            ],
            'limits_defaults' => [
                'daily'   => (float) ($_ENV['LIMIT_DAILY_DEFAULT'] ?? 1000),
                'weekly'  => (float) ($_ENV['LIMIT_WEEKLY_DEFAULT'] ?? 5000),
                'monthly' => (float) ($_ENV['LIMIT_MONTHLY_DEFAULT'] ?? 20000),
            ],
            'didit' => [
                'api_base'     => $_ENV['DIDIT_API_BASE'] ?? 'https://verification.didit.me',
                'api_key'      => $_ENV['DIDIT_API_KEY'] ?? '',
                'workflow_id'  => $_ENV['DIDIT_WORKFLOW_ID'] ?? '',
                'callback_url' => $_ENV['DIDIT_CALLBACK_URL'] ?? '',
                'return_url'   => $_ENV['DIDIT_RETURN_URL'] ?? '',
            ],
            'watched_tokens' => $watchedTokens,
            'admin_wallets' => [
                'ERC20' => $_ENV['ADMIN_WALLET_ERC20'] ?? '',
                'BEP20' => $_ENV['ADMIN_WALLET_BEP20'] ?? '',
                'TRC20' => $_ENV['ADMIN_WALLET_TRC20'] ?? '',
            ],
            // Optional path to an OpenSSL configuration file. Providing this
            // resolves secp256k1 generation issues on Windows environments.
            'openssl_config_path' => $_ENV['OPENSSL_CONFIG_PATH']
                ?? $_ENV['OPENSSL_CONF']
                ?? getenv('OPENSSL_CONF')
                ?? '',
            // Webhook URL for Telegram. Set WEBHOOK_URL in your .env when
            // deploying to a publicly reachable server.  If unset the bot
            // will use long polling via handleGetUpdates().
            'webhook_url'      => $_ENV['WEBHOOK_URL'] ?? '',
            'db' => [
                'host'     => $_ENV['DB_HOST'] ?? 'localhost',
                'port'     => $_ENV['DB_PORT'] ?? '3306',
                'database' => $_ENV['DB_NAME'] ?? 'p2pbot',
                'user'     => $_ENV['DB_USER'] ?? 'root',
                'password' => $_ENV['DB_PASS'] ?? '',
            ],
        ];
    }

    /**
     * Get the current configuration instance. If none exists yet, it is
     * created using the private constructor.
     *
     * @return self
     */
    public static function get(): self
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Retrieve a configuration value by key. Nested keys can be accessed by
     * passing the array path separated with dots. If the key does not exist,
     * the provided default value is returned.
     *
     * @param string $key     The configuration key (dot notation allowed).
     * @param mixed  $default Fallback value if key is not found.
     *
     * @return mixed
     */
    public function getValue(string $key, $default = null)
    {
        // Support dot notation for nested arrays.
        if (strpos($key, '.') !== false) {
            $segments = explode('.', $key);
            $value    = $this->data;
            foreach ($segments as $segment) {
                if (!array_key_exists($segment, $value)) {
                    return $default;
                }
                $value = $value[$segment];
            }
            return $value;
        }
        return $this->data[$key] ?? $default;
    }

    /**
     * @return array<string,array<string,mixed>>
     */
    private function buildTokenConfig(string $tokenString): array
    {
        $symbols = array_filter(array_map('trim', explode(',', $tokenString)));
        $config = [];
        foreach ($symbols as $symbol) {
            $key = strtoupper($symbol);
            if ($key === '') {
                continue;
            }
            $config[$key] = [
                'decimals' => (int) ($_ENV["TOKEN_{$key}_DECIMALS"] ?? 6),
                'ERC20'    => $_ENV["TOKEN_{$key}_ERC20_ADDRESS"] ?? '',
                'BEP20'    => $_ENV["TOKEN_{$key}_BEP20_ADDRESS"] ?? '',
                'TRC20'    => $_ENV["TOKEN_{$key}_TRC20_ADDRESS"] ?? '',
            ];
        }
        return $config;
    }
}
