<?php
namespace Bot\Commands\SystemCommands;

use Bot\Config;
use Bot\Database;
use Bot\Localization;
use Bot\Services\AdminWalletService;
use Bot\Services\BalanceService;
use Bot\Services\OrderService;
use Bot\Services\PaymentMethodService;
use Bot\Services\RateService;
use Bot\Services\ReferralService;
use Bot\Services\StateService;
use Bot\WalletService;
use Longman\TelegramBot\Commands\SystemCommand;
use Longman\TelegramBot\Entities\InlineKeyboard;
use Longman\TelegramBot\Entities\Keyboard;
use Longman\TelegramBot\Entities\ServerResponse;
use Longman\TelegramBot\Request;
use PDO;

class CallbackqueryCommand extends SystemCommand
{
    protected $name = 'callbackquery';
    protected $description = 'Handle callback query';
    protected $version = '3.0.0';

    private PDO $pdo;
    private StateService $stateService;
    private OrderService $orderService;
    private PaymentMethodService $paymentMethodService;
    private AdminWalletService $adminWalletService;
    private BalanceService $balanceService;
    private RateService $rateService;
    private ReferralService $referralService;

    public function __construct($telegram, $update)
    {
        parent::__construct($telegram, $update);
        $this->pdo = Database::connection();
        $this->stateService = new StateService();
        $this->orderService = new OrderService();
        $this->paymentMethodService = new PaymentMethodService();
        $this->adminWalletService = new AdminWalletService();
        $this->balanceService = new BalanceService();
        $this->rateService = new RateService();
        $this->referralService = new ReferralService();
    }

    public function execute(): ServerResponse
    {
        $expired = $this->orderService->expireStaleOrders();
        if ($expired) {
            $this->notifyExpiredOrders($expired);
        }

        $callback = $this->getCallbackQuery();
        $data = $callback->getData() ?? '';
        $chatId = $callback->getMessage()->getChat()->getId();
        $telegramId = $callback->getFrom()->getId();
        $stmt = $this->pdo->prepare('SELECT * FROM users WHERE telegram_id = ?');
        $stmt->execute([$telegramId]);
        $user = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$user) {
            return $this->notRegistered($callback->getId());
        }
        $lang = $user['language'] ?? 'en';
        $defaultNetwork = $user['default_network'] ?? Config::get()->getValue('default_network', 'ERC20');

        switch (true) {
            case $data === 'back_main':
                return $this->sendMainMenu($chatId, $lang);

            case $data === 'wallet_generate':
                return $this->handleWalletGenerate((int) $user['id'], $defaultNetwork, $chatId, $lang, $callback->getId());

            case $data === 'wallet_deposit':
                return $this->handleWalletDeposit((int) $user['id'], $defaultNetwork, $chatId, $lang, $callback->getId());

            case str_starts_with($data, 'lang_'):
                return $this->handleLanguageChange((int) $user['id'], substr($data, 5), $chatId);

            case $data === 'language':
                return $this->showLanguageSelector($chatId, $callback->getMessage()->getMessageId(), $lang);

            case $data === 'wallet_change_network':
                return $this->showNetworkSelectorMenu($chatId, $lang);

            case $data === 'wallet_balance':
                return $this->sendWalletSummaryMessage($user, $chatId, $lang);

            case $data === 'wallet_history':
                return $this->sendWalletHistory($user, $chatId, $lang);

            case $data === 'wallet_rates':
                return $this->sendRatesOverview($chatId, $lang);

            case $data === 'wallet_points':
                return $this->sendPointsSummary($user, $chatId);

            case $data === 'wallet_convert_points':
                return $this->startConvertPointsFlow($user, $chatId, $lang);

            case $data === 'start_withdraw':
                return $this->startWithdrawFlow($user, $chatId, $lang);

            case str_starts_with($data, 'network_'):
                return $this->handleNetworkChange((int) $user['id'], strtoupper(substr($data, 8)), $chatId, $lang, $callback->getId());

            case str_starts_with($data, 'paymethod_'):
                $response = $this->handlePayMethodSelection($user, (int) substr($data, 10), $chatId, $lang);
                Request::answerCallbackQuery([
                    'callback_query_id' => $callback->getId(),
                    'text'              => 'Payment method selected.',
                ]);
                return $response;

            case str_starts_with($data, 'sellpm_'):
                $response = $this->handleSellPayMethodSelection($user, (int) substr($data, 7), $chatId, $lang);
                Request::answerCallbackQuery([
                    'callback_query_id' => $callback->getId(),
                    'text'              => 'Payout method selected.',
                ]);
                return $response;

            case str_starts_with($data, 'sellnet_'):
                $response = $this->handleSellNetworkSelection($user, strtoupper(substr($data, 8)), $chatId, $lang);
                Request::answerCallbackQuery([
                    'callback_query_id' => $callback->getId(),
                    'text'              => 'Network selected.',
                ]);
                return $response;

            default:
                return Request::answerCallbackQuery([
                    'callback_query_id' => $callback->getId(),
                    'text'              => 'Feature coming soon.',
                    'show_alert'        => false,
                ]);
        }
    }

        private function sendMainMenu(int $chatId, string $lang): ServerResponse
    {
        $keyboard = new Keyboard(
            ['🅿️ P2P', '👛 Wallet'],
            ['💳 Virtual Card', '👤 My Profile'],
            ['🎁 Free', '🆘 Help & Support']
        );
        $keyboard->setResizeKeyboard(true)
                 ->setOneTimeKeyboard(false);
        return Request::sendMessage([
            'chat_id'      => $chatId,
            'text'         => Localization::t($lang, 'main_menu_prompt'),
            'reply_markup' => $keyboard,
        ]);
    }

    private function handleWalletGenerate(int $userId, string $network, int $chatId, string $lang, string $callbackId): ServerResponse
    {
        try {
            $walletService = new WalletService();
            $wallet = $walletService->createWallet($userId, $network);
        } catch (\Throwable $e) {
            return Request::sendMessage([
                'chat_id' => $chatId,
                'text'    => 'Wallet error: ' . $e->getMessage(),
            ]);
        }
        return Request::sendMessage([
            'chat_id' => $chatId,
            'text'    => Localization::t($lang, 'wallet_generated', [$network, $wallet['address']]),
        ]);
    }

    private function handleWalletDeposit(int $userId, string $network, int $chatId, string $lang, string $callbackId): ServerResponse
    {
        $walletService = new WalletService();
        $wallet = $walletService->getWallet($userId, $network);
        $message = $wallet
            ? Localization::t($lang, 'your_deposit_address', [$network, $wallet['address']])
            : Localization::t($lang, 'no_wallet_generate');
        return Request::sendMessage([
            'chat_id' => $chatId,
            'text'    => $message,
        ]);
    }

    private function notRegistered(string $callbackId): ServerResponse
    {
        return Request::answerCallbackQuery([
            'callback_query_id' => $callbackId,
            'text'              => 'Please send /start first.',
            'show_alert'        => true,
        ]);
    }

    private function showLanguageSelector(int $chatId, int $messageId, string $lang): ServerResponse
    {
        $keyboard = new InlineKeyboard(
            [['text' => 'English', 'callback_data' => 'lang_en']],
            [['text' => 'Bengali', 'callback_data' => 'lang_bn']],
            [['text' => 'Hindi', 'callback_data' => 'lang_hi']],
            [['text' => 'Urdu', 'callback_data' => 'lang_ur']],
            [['text' => '⬅️ Back', 'callback_data' => 'back_main']]
        );
        return Request::editMessageText([
            'chat_id'      => $chatId,
            'message_id'   => $messageId,
            'text'         => Localization::t($lang, 'choose_language'),
            'reply_markup' => $keyboard,
        ]);
    }

    private function handleLanguageChange(int $userId, string $newLang, int $chatId): ServerResponse
    {
        $stmt = $this->pdo->prepare('UPDATE users SET language = ? WHERE id = ?');
        $stmt->execute([$newLang, $userId]);
        return Request::sendMessage([
            'chat_id' => $chatId,
            'text'    => Localization::t($newLang, 'language_updated'),
        ]);
    }

    private function handleNetworkChange(int $userId, string $network, int $chatId, string $lang, string $callbackId): ServerResponse
    {
        $supported = Config::get()->getValue('supported_networks', ['ERC20', 'BEP20', 'TRC20']);
        if (!in_array($network, $supported, true)) {
            return Request::answerCallbackQuery([
                'callback_query_id' => $callbackId,
                'text'              => 'Unsupported network.',
                'show_alert'        => true,
            ]);
        }
        $stmt = $this->pdo->prepare('UPDATE users SET default_network = ? WHERE id = ?');
        $stmt->execute([$network, $userId]);
        return Request::sendMessage([
            'chat_id' => $chatId,
            'text'    => Localization::t($lang, 'network_changed', [$network]),
        ]);
    }

    private function handlePayMethodSelection(array $user, int $methodId, int $chatId, string $lang): ServerResponse
    {
        $state = $this->stateService->getState((int) $user['id']);
        if (!$state || $state['state'] !== 'await_payment_method') {
            return Request::sendMessage([
                'chat_id' => $chatId,
                'text'    => Localization::t($lang, 'unknown_command'),
            ]);
        }
        $method = $this->paymentMethodService->getById($methodId);
        if (!$method || !(int) $method['is_active']) {
            return Request::sendMessage([
                'chat_id' => $chatId,
                'text'    => Localization::t($lang, 'no_payment_methods'),
            ]);
        }
        $data = $state['data'] ?? [];
        $currency = $data['currency'] ?? 'USDT';
        $amount = (float) ($data['amount'] ?? 0);
        $mode = $data['mode'] ?? 'buy';
        if ($amount <= 0) {
            $this->stateService->clearState((int) $user['id']);
            return Request::sendMessage([
                'chat_id' => $chatId,
                'text'    => Localization::t($lang, 'unknown_command'),
            ]);
        }

        try {
            $order = $this->orderService->createOrder((int) $user['id'], $mode, $currency, $amount, $method);
        } catch (\Throwable $e) {
            $this->stateService->clearState((int) $user['id']);
            return Request::sendMessage([
                'chat_id' => $chatId,
                'text'    => Localization::t($lang, 'rate_missing', [$currency]),
            ]);
        }

        $this->stateService->setState((int) $user['id'], 'await_payment_proof', ['order_id' => $order['id']]);
        $this->notifyAdminsNewOrder($user, $order, $method);
        return $this->sendPaymentInstructions($chatId, $lang, $order, $method);
    }

    private function handleSellPayMethodSelection(array $user, int $methodId, int $chatId, string $lang): ServerResponse
    {
        $state = $this->stateService->getState((int) $user['id']);
        if (!$state || $state['state'] !== 'await_sell_payout_method') {
            return Request::sendMessage([
                'chat_id' => $chatId,
                'text'    => Localization::t($lang, 'unknown_command'),
            ]);
        }
        $method = $this->paymentMethodService->getById($methodId);
        if (!$method || $method['direction'] !== 'sell' || !(int) $method['is_active']) {
            return Request::sendMessage([
                'chat_id' => $chatId,
                'text'    => Localization::t($lang, 'sell_no_payment_methods'),
            ]);
        }
        $data = $state['data'] ?? [];
        $data['payout_method_id'] = $methodId;
        $data['payout_method_name'] = $method['name'];
        $data['source'] = $data['source'] ?? 'onchain';
        $this->stateService->setState((int) $user['id'], 'await_payout_details', $data);
        $prompt = $method['details_prompt']
            ?? Localization::t($lang, 'sell_enter_payout_details', [$method['name']]);
        return Request::sendMessage([
            'chat_id' => $chatId,
            'text'    => $prompt,
        ]);
    }

    private function handleSellNetworkSelection(array $user, string $network, int $chatId, string $lang): ServerResponse
    {
        $network = strtoupper($network);
        $supported = Config::get()->getValue('supported_networks', ['ERC20', 'BEP20', 'TRC20']);
        if (!in_array($network, $supported, true)) {
            return Request::sendMessage([
                'chat_id' => $chatId,
                'text'    => Localization::t($lang, 'sell_network_missing', [$network]),
            ]);
        }
        $state = $this->stateService->getState((int) $user['id']);
        if (!$state || $state['state'] !== 'await_crypto_network') {
            return Request::sendMessage([
                'chat_id' => $chatId,
                'text'    => Localization::t($lang, 'unknown_command'),
            ]);
        }
        $data = $state['data'] ?? [];
        $method = !empty($data['payout_method_id'])
            ? $this->paymentMethodService->getById((int) $data['payout_method_id'])
            : null;
        if (!$method) {
            $this->stateService->clearState((int) $user['id']);
            return Request::sendMessage([
                'chat_id' => $chatId,
                'text'    => Localization::t($lang, 'sell_no_payment_methods'),
            ]);
        }
        $address = $this->adminWalletService->getAddress($network);
        if (!$address) {
            return Request::sendMessage([
                'chat_id' => $chatId,
                'text'    => Localization::t($lang, 'sell_network_missing', [$network]),
            ]);
        }
        $amount = (float) ($data['amount'] ?? 0);
        $currency = $data['currency'] ?? 'USDT';
        $options = [
            'initial_payment_status' => 'awaiting_txid',
            'payout_details'         => $data['payout_details'] ?? '',
            'crypto_network'         => $network,
            'crypto_deposit_address' => $address,
            'crypto_status'          => 'awaiting_txid',
        ];
        $order = $this->orderService->createOrder(
            (int) $user['id'],
            'sell',
            $currency,
            $amount,
            $method,
            $options
        );
        $this->stateService->setState((int) $user['id'], 'await_txid', ['order_id' => $order['id']]);
        $this->notifyAdminsNewOrder($user, $order, $method, $data['payout_details'] ?? null);
        return $this->sendSellInstructions($chatId, $lang, $order, $options);
    }

    private function sendPaymentInstructions(int $chatId, string $lang, array $order, array $method): ServerResponse
    {
        [$deadlineText, $countdown] = $this->formatDeadlineInfo($order['payment_deadline'] ?? null);
        $text = Localization::t(
            $lang,
            'payment_instructions_message',
            [
                $order['id'],
                $method['name'],
                number_format($order['quote_total'], 2),
                $order['payment_instructions'] ?? $method['instructions'],
            ]
        );
        $contactText = trim((string) ($method['contact_details'] ?? ''));
        $contactLine = $contactText !== '' ? "\n" . Localization::t($lang, 'payment_contact_line', [$contactText]) : '';
        Request::sendMessage([
            'chat_id' => $chatId,
            'text'    => $text . $contactLine,
        ]);
        return Request::sendMessage([
            'chat_id' => $chatId,
            'text'    => Localization::t($lang, 'send_payment_proof_prompt', [$deadlineText, $countdown]),
        ]);
    }

    private function sendSellInstructions(int $chatId, string $lang, array $order, array $options): ServerResponse
    {
        [$deadlineText, $countdown] = $this->formatDeadlineInfo($order['payment_deadline'] ?? null);
        $text = Localization::t(
            $lang,
            'sell_crypto_instructions',
            [
                $order['id'],
                number_format($order['amount'], 2),
                strtoupper($order['currency']),
                $options['crypto_network'] ?? '',
                $options['crypto_deposit_address'] ?? '',
            ]
        );
        Request::sendMessage([
            'chat_id' => $chatId,
            'text'    => $text,
        ]);
        return Request::sendMessage([
            'chat_id' => $chatId,
            'text'    => Localization::t($lang, 'sell_txid_prompt', [$deadlineText, $countdown]),
        ]);
    }

    private function notifyAdminsNewOrder(array $user, array $order, array $method, ?string $payoutDetails = null): void
    {
        $admins = Config::get()->getValue('admin_ids', []);
        $kycStatus = strtolower($user['kyc_status'] ?? 'unverified');
        if ($kycStatus === 'verified') {
            $kycLine = '✅ Verified';
            if (!empty($user['kyc_session_id'])) {
                $kycLine .= ' (session ' . $user['kyc_session_id'] . ')';
            }
        } elseif ($kycStatus === 'pending') {
            $kycLine = '🕜 Pending';
        } else {
            $kycLine = '❌ Unverified';
        }

        [$deadlineText, $countdown] = $this->formatDeadlineInfo($order['payment_deadline'] ?? null);
        $base = sprintf(
            "🆕 %s order #%d\n👤 User: %s (UID %d)\n💱 Amount: %s %s\n💸 Payable: %s\n🏧 Method: %s\n🛡️ KYC: %s\n⏳ Deadline: %s (%s)",
            strtoupper($order['type']),
            $order['id'],
            $user['username'] ? '@' . $user['username'] : 'ID ' . $user['telegram_id'],
            $user['id'],
            number_format($order['amount'], 2),
            strtoupper($order['currency']),
            number_format($order['quote_total'], 2),
            $method['name'],
            $kycLine,
            $deadlineText,
            $countdown
        );
        if ($order['type'] === 'sell') {
            $base .= sprintf(
                "\n🌐 Network: %s\n💼 Admin wallet: %s\n📝 Payout details: %s\n🔁 TXID: %s",
                $order['crypto_network'] ?? 'N/A',
                $order['crypto_deposit_address'] ?? 'N/A',
                $payoutDetails ?: ($order['payout_details'] ?? 'N/A'),
                $order['txid'] ?? 'N/A'
            );
        }
        $text = $base . sprintf(
            "\n\nCommands:\n`/adminorders view %d`\n`/adminorders approve %d`\n`/adminorders reject %d <reason>`\n`/adminorders ask %d <question>`",
            $order['id'],
            $order['id'],
            $order['id'],
            $order['id']
        );
        foreach ($admins as $adminId) {
            if (!is_numeric($adminId)) {
                continue;
            }
            Request::sendMessage([
                'chat_id' => (int) $adminId,
                'text'    => $text,
            ]);
        }
    }

    private function sendWalletHistory(array $user, int $chatId, string $lang, int $limit = 10): ServerResponse
    {
        $entries = $this->balanceService->getLedger((int) $user['id'], $limit);
        if (!$entries) {
            return Request::sendMessage([
                'chat_id' => $chatId,
                'text'    => Localization::t($lang, 'wallet_history_empty'),
            ]);
        }
        $lines = [Localization::t($lang, 'wallet_history_title')];
        foreach ($entries as $entry) {
            $sign = $entry['direction'] === 'credit' ? '+' : '-';
            $amount = number_format((float) $entry['amount'], 4);
            $category = strtoupper($entry['category']);
            $when = (new \DateTime($entry['created_at']))->format('d M H:i');
            $lines[] = Localization::t(
                $lang,
                'wallet_history_line',
                [
                    $entry['currency'],
                    $sign,
                    $amount,
                    $category,
                    $when,
                ]
            );
        }
        return Request::sendMessage([
            'chat_id' => $chatId,
            'text'    => implode("\n", $lines),
        ]);
    }

    private function sendRatesOverview(int $chatId, string $lang): ServerResponse
    {
        $lines = [Localization::t($lang, 'wallet_rates_title')];
        $hasRates = false;
        foreach ($this->rateService->getSupportedCurrencies() as $currency) {
            $rates = $this->rateService->getRates($currency);
            if (!$rates) {
                continue;
            }
            $hasRates = true;
            $lines[] = Localization::t(
                $lang,
                'wallet_rates_line',
                [
                    strtoupper($currency),
                    number_format($rates['rate_buy'], 2),
                    number_format($rates['rate_sell'], 2),
                ]
            );
        }
        if (!$hasRates) {
            $lines[] = Localization::t($lang, 'wallet_rates_empty');
        }
        return Request::sendMessage([
            'chat_id' => $chatId,
            'text'    => implode("\n", $lines),
        ]);
    }

    private function sendWalletSummaryMessage(array $user, int $chatId, string $lang): ServerResponse
    {
        $balances = $this->balanceService->listBalances((int) $user['id']);
        if (!$balances) {
            return Request::sendMessage([
                'chat_id' => $chatId,
                'text'    => Localization::t($lang, 'wallet_summary_empty'),
            ]);
        }
        $lines = [Localization::t($lang, 'wallet_summary_title')];
        foreach ($balances as $row) {
            $lines[] = Localization::t(
                $lang,
                'wallet_summary_line',
                [
                    $row['currency'],
                    number_format((float) $row['available'], 4),
                    number_format((float) $row['locked'], 4),
                ]
            );
        }
        return Request::sendMessage([
            'chat_id' => $chatId,
            'text'    => implode("\n", $lines),
        ]);
    }

    private function sendPointsSummary(array $user, int $chatId): ServerResponse
    {
        $lang = $user['language'] ?? 'en';
        $pointsBalance = $this->balanceService->getBalance((int) $user['id'], 'POINT');
        $points = number_format((float) $pointsBalance['available'], 4);
        $rate = $this->referralService->getPointRate();
        $usd = number_format((float) $pointsBalance['available'] * $rate, 2);
        $text = sprintf(
            "Referral Level: %s\nPoints: %s (≈ %s USDT)\nUse /convertpoints <amount> to convert points into USDT.",
            $user['referral_level'] ?? 'Bronze',
            $points,
            $usd
        );
        return Request::sendMessage([
            'chat_id' => $chatId,
            'text'    => $text,
        ]);
    }

    private function startConvertPointsFlow(array $user, int $chatId, string $lang): ServerResponse
    {
        $this->stateService->setState((int) $user['id'], 'await_convert_points_amount');
        return Request::sendMessage([
            'chat_id' => $chatId,
            'text'    => Localization::t($lang, 'convert_points_prompt'),
        ]);
    }

    private function startWithdrawFlow(array $user, int $chatId, string $lang): ServerResponse
    {
        if (($user['kyc_status'] ?? 'unverified') !== 'verified') {
            return Request::sendMessage([
                'chat_id' => $chatId,
                'text'    => Localization::t($lang, 'withdraw_kyc_required'),
            ]);
        }
        $currency = 'USDT';
        $network = strtoupper($user['default_network'] ?? 'ERC20');
        $balance = $this->balanceService->getBalance((int) $user['id'], $currency);
        $available = $balance['available'];
        $this->stateService->setState((int) $user['id'], 'await_withdraw_amount', [
            'currency' => $currency,
            'network'  => $network,
        ]);
        return Request::sendMessage([
            'chat_id' => $chatId,
            'text'    => Localization::t($lang, 'withdraw_enter_amount', [$currency, $available]),
        ]);
    }

    private function showNetworkSelectorMenu(int $chatId, string $lang): ServerResponse
    {
        $networks = Config::get()->getValue('supported_networks', ['ERC20', 'BEP20', 'TRC20']);
        $buttons = [];
        foreach ($networks as $net) {
            $buttons[] = [['text' => $net, 'callback_data' => 'network_' . strtoupper($net)]];
        }
        $buttons[] = [['text' => '⬅️ Back', 'callback_data' => 'back_main']];
        return Request::sendMessage([
            'chat_id'      => $chatId,
            'text'         => Localization::t($lang, 'network_prompt'),
            'reply_markup' => new InlineKeyboard(...$buttons),
        ]);
    }
/**
     * @return array{0:string,1:string}
     */
    private function formatDeadlineInfo(?string $deadline): array
    {
        if (!$deadline) {
            return ['N/A', '0m'];
        }
        $deadlineDt = new \DateTimeImmutable($deadline);
        $now = new \DateTimeImmutable('now');
        $formatted = $deadlineDt->format('Y-m-d H:i');
        $seconds = $deadlineDt->getTimestamp() - $now->getTimestamp();
        if ($seconds <= 0) {
            return [$formatted, '0m'];
        }
        $minutes = intdiv($seconds, 60);
        $hours = intdiv($minutes, 60);
        $mins = $minutes % 60;
        $parts = [];
        if ($hours > 0) {
            $parts[] = $hours . 'h';
        }
        if ($mins > 0) {
            $parts[] = $mins . 'm';
        }
        if ($hours === 0 && $mins === 0) {
            $parts[] = ($seconds % 60) . 's';
        }
        return [$formatted, implode(' ', $parts) ?: '0m'];
    }

    /**
     * @param array<int,array{id:int,telegram_id:string,language:string}> $orders
     */
    private function notifyExpiredOrders(array $orders): void
    {
        foreach ($orders as $order) {
            if (empty($order['telegram_id'])) {
                continue;
            }
            [$deadlineText] = $this->formatDeadlineInfo($order['payment_deadline'] ?? null);
            Request::sendMessage([
                'chat_id' => $order['telegram_id'],
                'text'    => Localization::t(
                    $order['language'] ?? 'en',
                    'order_cancelled_due_timeout',
                    [$order['id'], $deadlineText]
                ),
            ]);
        }
    }
}
