<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\User;
use App\Models\UserBalance;
use Illuminate\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\DB;
use App\Services\BinanceService;

class FinancialController extends Controller
{
    private static function fallbackPrice(string $coin): float
    {
        $map = [
            'USDT' => 1.0, 'BTC' => 95000.0, 'ETH' => 3500.0, 'BNB' => 600.0,
            'SOL' => 220.0, 'XRP' => 2.0, 'ADA' => 0.9, 'DOGE' => 0.4,
            'DOT' => 7.0, 'LTC' => 100.0, 'AVAX' => 35.0, 'TRX' => 0.25,
            'LINK' => 18.0, 'BCH' => 450.0, 'XLM' => 0.4, 'UNI' => 10.0, 'ATOM' => 8.0,
        ];
        return $map[strtoupper($coin)] ?? 0.0;
    }

    public function __construct(
        private BinanceService $binanceService
    ) {}

    public function index(Request $request): View
    {
        $query = User::query();

        if ($request->has('search')) {
            $search = $request->get('search');
            $query->where('wallet_address', 'like', "%{$search}%");
        }

        $perPage = $request->get('per_page', 10);
        if (!in_array($perPage, [10, 20, 50, 100])) {
            $perPage = 10;
        }

        $users = $query->with('userBalances')->latest()->paginate($perPage)->withQueryString();

        $totalPortfolioByUser = [];
        $coins = [];
        foreach ($users as $user) {
            foreach ($user->userBalances as $ub) {
                $c = strtoupper($ub->coin_name);
                $coins[$c] = true;
            }
        }
        $prices = [];
        foreach (array_keys($coins) as $coin) {
            if ($coin === 'USDT') {
                $prices[$coin] = 1.0;
                continue;
            }
            $price = $this->binanceService->getCoinPrice($coin);
            $prices[$coin] = $price !== null ? (float) $price : self::fallbackPrice($coin);
        }
        foreach ($users as $user) {
            $total = 0.0;
            foreach ($user->userBalances as $ub) {
                $price = $prices[strtoupper($ub->coin_name)] ?? 0.0;
                $total += (float) $ub->balance * $price;
            }
            $totalPortfolioByUser[$user->id] = $total;
        }

        return view('admin.financial.index', [
            'users' => $users,
            'totalPortfolioByUser' => $totalPortfolioByUser,
            'allowedCoins' => self::allowedCoins(),
            'pageTitle' => __('admin.nav.financial'),
            'title' => 'Financial Management'
        ]);
    }

    /** Allowed coins for balance add/deduct (user_balances.coin_name). */
    private static function allowedCoins(): array
    {
        return ['USDT', 'BTC', 'ETH', 'BNB', 'SOL', 'XRP', 'ADA', 'DOGE', 'DOT', 'LTC', 'AVAX', 'TRX', 'LINK', 'BCH', 'XLM', 'UNI', 'ATOM', 'USDC', 'MATIC'];
    }

    /**
     * Add or deduct balance for any coin. Single user or all users (apply_to_all=1).
     * USDT also updates legacy users.balance.
     */
    public function adjust(Request $request): RedirectResponse
    {
        $applyToAll = $request->boolean('apply_to_all');
        $rules = [
            'coin_name' => 'required|string|max:20',
            'type' => 'required|in:balance_add,balance_deduct',
            'amount' => 'required|numeric|min:0',
            'reason' => 'required|string|max:500',
        ];
        if (!$applyToAll) {
            $rules['user_id'] = 'required|exists:users,id';
        }
        $validated = $request->validate($rules);

        $coin = strtoupper(trim($validated['coin_name']));
        if (!in_array($coin, self::allowedCoins(), true)) {
            return redirect()->back()->withErrors(['coin_name' => 'Invalid coin. Allowed: ' . implode(', ', self::allowedCoins())])->withInput();
        }

        $amount = (float) str_replace(',', '', (string) $validated['amount']);
        if ($amount < 0) {
            return redirect()->back()->withErrors(['amount' => 'Amount must be positive.'])->withInput();
        }

        if ($applyToAll) {
            return $this->adjustAllUsers($validated, $coin, $amount);
        }

        $user = User::findOrFail($validated['user_id']);
        $userBalance = UserBalance::firstOrNew(['user_id' => $user->id, 'coin_name' => $coin]);
        if (!$userBalance->exists) {
            $userBalance->balance = 0;
            $userBalance->locked_balance = 0;
        }
        $balanceBefore = (float) $userBalance->balance;
        if ($validated['type'] === 'balance_deduct' && $balanceBefore < $amount) {
            return redirect()->back()
                ->withErrors(['amount' => 'Insufficient balance. Available: ' . number_format($balanceBefore, 8) . ' ' . $coin])
                ->withInput();
        }

        DB::transaction(function () use ($user, $validated, $amount, $balanceBefore, $coin) {
            $this->applyAdjustToUser($user, $coin, $validated['type'], $amount, $validated['reason'], $balanceBefore);
        });

        return redirect()->back()->with('success', $coin . ' balance adjustment completed for 1 user.');
    }

    /**
     * Apply same add/deduct to all users. Deduct skips users with insufficient balance.
     */
    private function adjustAllUsers(array $validated, string $coin, float $amount): RedirectResponse
    {
        $users = User::orderBy('id')->get();
        $updated = 0;
        $skipped = 0;

        foreach ($users as $user) {
            $userBalance = UserBalance::firstOrNew(['user_id' => $user->id, 'coin_name' => $coin]);
            if (!$userBalance->exists) {
                $userBalance->balance = 0;
                $userBalance->locked_balance = 0;
            }
            $balanceBefore = (float) $userBalance->balance;
            if ($validated['type'] === 'balance_deduct' && $balanceBefore < $amount) {
                $skipped++;
                continue;
            }
            DB::transaction(function () use ($user, $coin, $validated, $amount, $balanceBefore) {
                $this->applyAdjustToUser($user, $coin, $validated['type'], $amount, $validated['reason'], $balanceBefore);
            });
            $updated++;
        }

        $msg = $coin . ' balance adjustment completed: ' . $updated . ' user(s) updated.';
        if ($skipped > 0) {
            $msg .= ' ' . $skipped . ' user(s) skipped (insufficient balance for deduct).';
        }
        return redirect()->back()->with('success', $msg);
    }

    private function applyAdjustToUser(User $user, string $coin, string $type, float $amount, string $reason, ?float $balanceBefore = null): void
    {
        $userBalance = UserBalance::firstOrNew(['user_id' => $user->id, 'coin_name' => $coin]);
        if (!$userBalance->exists) {
            $userBalance->balance = 0;
            $userBalance->locked_balance = 0;
            $userBalance->save();
        }
        if ($balanceBefore === null) {
            $balanceBefore = (float) $userBalance->balance;
        }
        if ($type === 'balance_add') {
            $userBalance->increment('balance', $amount);
            if ($coin === 'USDT') {
                $user->increment('balance', $amount);
            }
        } else {
            $userBalance->decrement('balance', $amount);
            if ($coin === 'USDT') {
                $user->decrement('balance', $amount);
            }
        }
        $userBalance->refresh();
        $balanceAfter = (float) $userBalance->balance;
        $adminUserId = session('admin.user_id');
        if ($adminUserId) {
            \App\Models\AuditLog::create([
                'admin_user_id' => $adminUserId,
                'action' => 'financial_adjust',
                'model_type' => User::class,
                'model_id' => $user->id,
                'old_values' => ['coin' => $coin, 'type' => $type, 'amount' => $amount, 'balance_before' => $balanceBefore],
                'new_values' => ['balance_after' => $balanceAfter],
                'reason' => $reason,
            ]);
        }
    }
}
