<?php
namespace Bot\Commands\AdminCommands;

use Bot\Localization;
use Bot\Services\AdminActivityService;
use Bot\Services\AdminUserService;
use Bot\Services\BalanceHoldService;
use Bot\Services\BalanceService;
use Bot\Services\OrderService;
use Bot\Services\WithdrawalService;
use Longman\TelegramBot\Commands\AdminCommand;
use Longman\TelegramBot\Entities\ServerResponse;
use Longman\TelegramBot\Request;

class AdminusersCommand extends AdminCommand
{
    protected $name = 'adminusers';
    protected $description = 'Manage and inspect users';
    protected $usage = '/adminusers search <term>';
    protected $version = '1.0.0';

    private AdminUserService $users;
    private BalanceService $balances;
    private BalanceHoldService $holds;
    private OrderService $orders;
    private WithdrawalService $withdrawals;
    private AdminActivityService $activity;

    public function __construct($telegram, $update)
    {
        parent::__construct($telegram, $update);
        $this->users = new AdminUserService();
        $this->balances = new BalanceService();
        $this->holds = new BalanceHoldService();
        $this->orders = new OrderService();
        $this->withdrawals = new WithdrawalService();
        $this->activity = new AdminActivityService();
    }

    public function execute(): ServerResponse
    {
        $message = $this->getMessage();
        $chatId = $message->getChat()->getId();
        $text = trim($message->getText(true) ?? '');
        if ($text === '') {
            return $this->replyUsage();
        }
        [$command, $payload] = array_pad(explode(' ', $text, 2), 2, '');
        $command = strtolower($command);
        return match ($command) {
            'search'      => $this->handleSearch($payload),
            'all'         => $this->handleListAll($payload),
            'view'        => $this->handleView($payload),
            'ban'         => $this->handleBan($payload, true),
            'unban'       => $this->handleBan($payload, false),
            'holds'       => $this->handleListHolds($payload),
            'hold'        => $this->handleCreateHold($payload),
            'releasehold' => $this->handleReleaseHold($payload),
            'txns'        => $this->handleTransactions($payload),
            'orders'      => $this->handleOrders($payload),
            'withdraws'   => $this->handleWithdrawals($payload),
            'credit'      => $this->handleAdjustBalance($payload, true),
            'debit'       => $this->handleAdjustBalance($payload, false),
            default       => $this->replyUsage(),
        };
    }

    private function replyUsage(): ServerResponse
    {
        $text = "Admin user commands:\n"
            . " `/adminusers search <term>`\n"
            . " `/adminusers all [page]`\n"
            . " `/adminusers view <user_id|username>`\n"
            . " `/adminusers ban <user_id> <reason>`\n"
            . " `/adminusers unban <user_id>`\n"
            . " `/adminusers hold <user_id> <amount> <currency> <reason>`\n"
            . " `/adminusers releasehold <hold_id>`\n"
            . " `/adminusers credit <user_id> <amount> <currency> <reason>`\n"
            . " `/adminusers debit <user_id> <amount> <currency> <reason>`\n"
            . " `/adminusers holds <user_id>`\n"
            . " `/adminusers txns <user_id>`\n"
            . " `/adminusers orders <user_id>`\n"
            . " `/adminusers withdraws <user_id>`";
        return $this->sendReply($text);
    }

    private function handleSearch(string $term): ServerResponse
    {
        $term = trim($term);
        if ($term === '') {
            return $this->sendReply('Usage: `/adminusers search <term>`');
        }
        $results = $this->users->search($term);
        if (!$results) {
            return $this->sendReply('No users found.');
        }
        $lines = [];
        foreach ($results as $row) {
            $lines[] = sprintf(
                '#%d @%s (%s) KYC:%s %s',
                $row['id'],
                $row['username'] ?: '-',
                $row['telegram_id'],
                strtoupper($row['kyc_status']),
                $row['is_banned'] ? '🚫' : ''
            );
        }
        return $this->sendReply(implode("\n", $lines));
    }

    private function handleListAll(string $pageInput): ServerResponse
    {
        $perPage = 25;
        $page = (int) trim($pageInput);
        if ($page <= 0) {
            $page = 1;
        }
        $offset = ($page - 1) * $perPage;
        $rows = $this->users->listAll($perPage, $offset);
        if (!$rows) {
            return $this->sendReply(sprintf('No users found on page %d.', $page));
        }
        $lines = [];
        foreach ($rows as $row) {
            $lines[] = sprintf(
                '#%d @%s (%s) KYC:%s %s',
                $row['id'],
                $row['username'] ?: '-',
                $row['telegram_id'],
                strtoupper($row['kyc_status']),
                $row['is_banned'] ? '🚫' : ''
            );
        }
        $lines[] = sprintf('--- Page %d (showing %d) ---', $page, count($rows));
        $lines[] = 'Use `/adminusers all ' . ($page + 1) . '` for next page.';
        return $this->sendReply(implode("\n", $lines));
    }

    private function handleView(string $identifier): ServerResponse
    {
        $user = $this->resolveUser($identifier);
        if (!$user) {
            return $this->sendReply('User not found.');
        }
        $balance = $this->balances->listBalances((int) $user['id']);
        $balanceLines = array_map(static function ($row) {
            return sprintf('%s: %s / locked %s', $row['currency'], $row['available'], $row['locked']);
        }, $balance);
        $text = sprintf(
            "User #%d\nTG: %s\nUsername: @%s\nKYC: %s\nBanned: %s\nReason: %s\nBalances:\n%s",
            $user['id'],
            $user['telegram_id'],
            $user['username'] ?? '-',
            ucfirst($user['kyc_status']),
            $user['is_banned'] ? 'Yes' : 'No',
            $user['ban_reason'] ?? '-',
            $balanceLines ? implode("\n", $balanceLines) : 'None'
        );
        return $this->sendReply($text);
    }

    private function handleBan(string $payload, bool $ban): ServerResponse
    {
        [$identifier, $reason] = array_pad(explode(' ', $payload, 2), 2, '');
        if ($identifier === '' || ($ban && $reason === '')) {
            return $this->sendReply($ban
                ? 'Usage: `/adminusers ban <user_id|username> <reason>`'
                : 'Usage: `/adminusers unban <user_id|username>`');
        }
        $user = $this->resolveUser($identifier);
        if (!$user) {
            return $this->sendReply('User not found.');
        }
        $this->users->setBanStatus((int) $user['id'], $ban, $ban ? $reason : null);
        $this->activity->log(
            (int) $this->getMessage()->getFrom()->getId(),
            $ban ? 'ban' : 'unban',
            (int) $user['id'],
            ['reason' => $reason]
        );
        return $this->sendReply($ban ? 'User banned.' : 'User unbanned.');
    }

    private function handleCreateHold(string $payload): ServerResponse
    {
        [$identifier, $amount, $currency, $reason] = array_map('trim', explode(' ', $payload, 4) + ['', '', '', '']);
        if ($identifier === '' || $amount === '' || $currency === '' || $reason === '') {
            return $this->sendReply('Usage: `/adminusers hold <user_id> <amount> <currency> <reason>`');
        }
        if (!is_numeric($amount) || (float) $amount <= 0) {
            return $this->sendReply('Amount must be numeric.');
        }
        $user = $this->resolveUser($identifier);
        if (!$user) {
            return $this->sendReply('User not found.');
        }
        try {
            $holdId = $this->holds->createHold((int) $user['id'], strtoupper($currency), number_format((float) $amount, 6, '.', ''), $reason);
        } catch (\Throwable $e) {
            return $this->sendReply('Unable to create hold: ' . $e->getMessage());
        }
        $this->activity->log(
            (int) $this->getMessage()->getFrom()->getId(),
            'hold_create',
            (int) $user['id'],
            ['hold_id' => $holdId, 'amount' => $amount, 'currency' => $currency, 'reason' => $reason]
        );
        return $this->sendReply("Hold created (#{$holdId}).");
    }

    private function handleReleaseHold(string $payload): ServerResponse
    {
        $holdId = (int) trim($payload);
        if ($holdId <= 0) {
            return $this->sendReply('Usage: `/adminusers releasehold <hold_id>`');
        }
        if (!$this->holds->releaseHold($holdId)) {
            return $this->sendReply('Hold not found or already released.');
        }
        $this->activity->log(
            (int) $this->getMessage()->getFrom()->getId(),
            'hold_release',
            null,
            ['hold_id' => $holdId]
        );
        return $this->sendReply('Hold released.');
    }

    private function handleListHolds(string $identifier): ServerResponse
    {
        $user = $this->resolveUser($identifier);
        if (!$user) {
            return $this->sendReply('User not found.');
        }
        $holds = $this->holds->listHolds((int) $user['id']);
        if (!$holds) {
            return $this->sendReply('No holds.');
        }
        $lines = [];
        foreach ($holds as $hold) {
            $lines[] = sprintf(
                '#%d %s %s (%s) %s',
                $hold['id'],
                $hold['currency'],
                $hold['amount'],
                $hold['status'],
                $hold['reason'] ?? ''
            );
        }
        return $this->sendReply(implode("\n", $lines));
    }

    private function handleTransactions(string $identifier): ServerResponse
    {
        $user = $this->resolveUser($identifier);
        if (!$user) {
            return $this->sendReply('User not found.');
        }
        $entries = $this->balances->getLedger((int) $user['id'], 10);
        if (!$entries) {
            return $this->sendReply('No ledger entries.');
        }
        $lines = [];
        foreach ($entries as $entry) {
            $lines[] = sprintf(
                "%s %s%s (%s) %s",
                $entry['currency'],
                $entry['direction'] === 'credit' ? '+' : '-',
                $entry['amount'],
                $entry['category'],
                $entry['created_at']
            );
        }
        return $this->sendReply(implode("\n", $lines));
    }

    private function handleAdjustBalance(string $payload, bool $credit): ServerResponse
    {
        [$identifier, $amountText, $currency, $reason] = array_pad(explode(' ', $payload, 4), 4, '');
        if ($identifier === '' || $amountText === '' || $currency === '' || $reason === '') {
            return $this->sendReply($credit
                ? 'Usage: `/adminusers credit <user_id> <amount> <currency> <reason>`'
                : 'Usage: `/adminusers debit <user_id> <amount> <currency> <reason>`');
        }
        if (!is_numeric($amountText) || (float) $amountText <= 0) {
            return $this->sendReply('Amount must be numeric and positive.');
        }
        $user = $this->resolveUser($identifier);
        if (!$user) {
            return $this->sendReply('User not found.');
        }
        $amount = number_format((float) $amountText, 6, '.', '');
        try {
            if ($credit) {
                $this->balances->credit((int) $user['id'], strtoupper($currency), $amount, 'admin_credit', null, ['reason' => $reason]);
            } else {
                $this->balances->debit((int) $user['id'], strtoupper($currency), $amount, 'admin_debit', null, ['reason' => $reason]);
            }
        } catch (\Throwable $e) {
            return $this->sendReply('Balance update failed: ' . $e->getMessage());
        }
        $this->activity->log(
            (int) $this->getMessage()->getFrom()->getId(),
            $credit ? 'manual_credit' : 'manual_debit',
            (int) $user['id'],
            ['amount' => $amount, 'currency' => strtoupper($currency), 'reason' => $reason]
        );
        return $this->sendReply($credit ? 'Funds credited.' : 'Funds debited.');
    }

    private function handleOrders(string $identifier): ServerResponse
    {
        $user = $this->resolveUser($identifier);
        if (!$user) {
            return $this->sendReply('User not found.');
        }
        $rows = $this->users->listOrders((int) $user['id']);
        if (!$rows) {
            return $this->sendReply('No orders.');
        }
        $lines = [];
        foreach ($rows as $row) {
            $lines[] = sprintf(
                '#%d %s %s %.2f status:%s pay:%s',
                $row['id'],
                strtoupper($row['type']),
                $row['currency'],
                $row['amount'],
                $row['status'],
                $row['payment_status']
            );
        }
        return $this->sendReply(implode("\n", $lines));
    }

    private function handleWithdrawals(string $identifier): ServerResponse
    {
        $user = $this->resolveUser($identifier);
        if (!$user) {
            return $this->sendReply('User not found.');
        }
        $rows = $this->users->listWithdrawals((int) $user['id']);
        if (!$rows) {
            return $this->sendReply('No withdrawals.');
        }
        $lines = [];
        foreach ($rows as $row) {
            $lines[] = sprintf(
                '#%d %s %s %s (%s) %s | review:%s',
                $row['id'],
                $row['currency'],
                $row['amount'],
                $row['status'],
                $row['network'],
                $row['to_address'],
                $row['review_status'] ?? 'pending'
            );
        }
        return $this->sendReply(implode("\n", $lines));
    }

    private function resolveUser(string $identifier): ?array
    {
        $identifier = trim($identifier);
        if ($identifier === '') {
            return null;
        }
        return $this->users->findByIdentifier(ltrim($identifier, '@'));
    }

    private function sendReply(string $text): ServerResponse
    {
        return Request::sendMessage([
            'chat_id' => $this->getMessage()->getChat()->getId(),
            'text'    => $text,
        ]);
    }
}
