<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Market;
use App\Models\Order;
use App\Models\Wallet;
use App\Models\Trade;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
use App\Services\RealTimeSyncService;

class TradingController extends Controller
{
    /**
     * Place a new order
     */
    public function placeOrder(Request $request): JsonResponse
    {
        try {
            $validated = $request->validate([
                'market_id' => 'required|exists:markets,id',
                'side' => 'required|in:buy,sell',
                'type' => 'required|in:limit,market,stop_limit,stop_market',
                'price' => 'required_if:type,limit,stop_limit|numeric|min:0',
                'quantity' => 'required|numeric|min:0.00000001',
                'stop_price' => 'nullable|numeric|min:0|required_if:type,stop_limit,stop_market',
                'time_in_force' => 'in:GTC,IOC,FOK,GTD',
                'client_order_id' => 'nullable|string|unique:orders,client_order_id',
            ]);

            $user = $request->user();
            $market = Market::findOrFail($validated['market_id']);

            // Begin database transaction
            $order = DB::transaction(function () use ($user, $market, $validated) {
                // Validate minimum order size
                if ($market->min_order_size > 0) {
                    $orderValue = $validated['price'] * $validated['quantity'];
                    if ($orderValue < $market->min_order_size) {
                        throw new \Exception('Order size below minimum allowed');
                    }
                }

                // Validate user has sufficient funds
                $requiredAmount = $this->calculateRequiredAmount(
                    $validated['side'], 
                    $validated['type'], 
                    $validated['price'] ?? 0, 
                    $validated['quantity']
                );

                $currency = $validated['side'] === 'buy' ? $market->quote_currency : $market->base_currency;
                $wallet = Wallet::forUserAndCurrency($user->id, $currency)->firstOrFail();

                if (!$wallet->hasSufficientFunds($requiredAmount)) {
                    throw new \Exception('Insufficient funds');
                }

                // Create the order
                $order = Order::create([
                    'user_id' => $user->id,
                    'market_id' => $market->id,
                    'side' => $validated['side'],
                    'type' => $validated['type'],
                    'price' => $validated['price'] ?? null,
                    'quantity' => $validated['quantity'],
                    'stop_price' => $validated['stop_price'] ?? null,
                    'time_in_force' => $validated['time_in_force'] ?? 'GTC',
                    'client_order_id' => $validated['client_order_id'] ?? null,
                ]);

                // Lock funds based on order type and side
                $this->lockOrderFunds($order, $wallet);

                // Synchronize the new order
                $syncService = new RealTimeSyncService();
                $syncService->syncOrder($order, 'create');

                return $order;
            });

            return response()->json([
                'success' => true,
                'data' => $order->load(['user', 'market'])
            ], 201);
        } catch (ValidationException $e) {
            return response()->json([
                'success' => false,
                'message' => 'Validation error',
                'errors' => $e->errors()
            ], 422);
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage()
            ], 400);
        }
    }

    /**
     * Cancel an order
     */
    public function cancelOrder(Request $request, string $orderId): JsonResponse
    {
        try {
            $user = $request->user();
            $order = Order::where('id', $orderId)
                         ->where('user_id', $user->id)
                         ->firstOrFail();

            if (!$order->isActive()) {
                return response()->json([
                    'success' => false,
                    'message' => 'Order cannot be cancelled'
                ], 400);
            }

            DB::transaction(function () use ($order) {
                // Release any locked funds
                foreach ($order->walletLocks as $lock) {
                    $wallet = Wallet::findOrFail($lock->wallet_id);
                    $wallet->releaseFunds($lock->id);
                }

                // Update order status
                $order->status = 'cancelled';
                $order->save();
            });

            return response()->json([
                'success' => true,
                'data' => $order->refresh()
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage()
            ], 400);
        }
    }

    /**
     * Get user's open orders
     */
    public function getOpenOrders(Request $request): JsonResponse
    {
        $user = $request->user();
        $orders = Order::where('user_id', $user->id)
                      ->active()
                      ->with(['market'])
                      ->latest()
                      ->paginate(50);

        return response()->json([
            'success' => true,
            'data' => $orders
        ]);
    }

    /**
     * Get user's order history
     */
    public function getOrderHistory(Request $request): JsonResponse
    {
        $user = $request->user();
        $orders = Order::where('user_id', $user->id)
                      ->completed()
                      ->with(['market'])
                      ->latest()
                      ->paginate(50);

        return response()->json([
            'success' => true,
            'data' => $orders
        ]);
    }

    /**
     * Get market depth for a specific market
     */
    public function getMarketDepth(string $marketId): JsonResponse
    {
        try {
            $market = Market::findOrFail($marketId);

            // Get open orders for the market
            $bids = Order::forMarket($marketId)
                         ->buy()
                         ->active()
                         ->orderByDesc('price')
                         ->orderBy('created_at')
                         ->limit(50)
                         ->get();

            $asks = Order::forMarket($marketId)
                         ->sell()
                         ->active()
                         ->orderBy('price')
                         ->orderBy('created_at')
                         ->limit(50)
                         ->get();

            // Aggregate orders by price level
            $bidLevels = $this->aggregatePriceLevels($bids);
            $askLevels = $this->aggregatePriceLevels($asks);

            return response()->json([
                'success' => true,
                'data' => [
                    'market' => $market,
                    'bids' => $bidLevels,
                    'asks' => $askLevels,
                    'spread' => count($askLevels) > 0 && count($bidLevels) > 0 
                        ? $askLevels[0]['price'] - $bidLevels[0]['price'] 
                        : null,
                ]
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage()
            ], 400);
        }
    }

    /**
     * Get recent trades for a market
     */
    public function getMarketTrades(string $marketId): JsonResponse
    {
        try {
            $market = Market::findOrFail($marketId);
            
            $trades = Trade::forMarket($marketId)
                          ->with(['buyOrder.user', 'sellOrder.user'])
                          ->latest('executed_at')
                          ->limit(100)
                          ->get();

            return response()->json([
                'success' => true,
                'data' => $trades
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage()
            ], 400);
        }
    }

    /**
     * Calculate required amount for order
     */
    private function calculateRequiredAmount(string $side, string $type, float $price, float $quantity): float
    {
        if ($side === 'buy') {
            // For buy orders, need price * quantity + fees
            $orderValue = $price * $quantity;
            // Add estimated fee (0.2% for takers)
            return $orderValue * 1.002;
        } else {
            // For sell orders, need quantity of base currency
            return $quantity;
        }
    }

    /**
     * Lock funds for an order
     */
    private function lockOrderFunds(Order $order, Wallet $wallet): void
    {
        if ($order->side === 'buy') {
            // For buy orders, lock quote currency (price * quantity + fees)
            $orderValue = $order->price * $order->quantity;
            $estimatedFee = $orderValue * 0.002; // 0.2% fee
            $totalAmount = $orderValue + $estimatedFee;
            
            $wallet->lockFunds($totalAmount, "Order {$order->id} buy {$order->quantity}");
        } else {
            // For sell orders, lock base currency (quantity)
            $wallet->lockFunds($order->quantity, "Order {$order->id} sell {$order->quantity}");
        }
    }

    /**
     * Aggregate orders by price levels
     */
    private function aggregatePriceLevels($orders)
    {
        $levels = [];
        $priceGroups = [];

        foreach ($orders as $order) {
            $price = (float) $order->price;
            $quantity = (float) $order->quantity - (float) $order->filled_quantity;
            
            if (isset($priceGroups[$price])) {
                $priceGroups[$price] += $quantity;
            } else {
                $priceGroups[$price] = $quantity;
            }
        }

        foreach ($priceGroups as $price => $totalQuantity) {
            $levels[] = [
                'price' => $price,
                'quantity' => $totalQuantity,
                'total' => $price * $totalQuantity,
            ];
        }

        return $levels;
    }
}