<?php

namespace App\Services;

use App\Models\Market;
use App\Models\Order;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\ValidationException;

class OrderValidationService
{
    // Minimum order sizes by market
    private const MIN_ORDER_SIZES = [
        'BTC-KES' => 0.0001,
        'ETH-KES' => 0.001,
        'USDT-KES' => 1,
        'default' => 0.001
    ];

    // Price precision by market (decimal places)
    private const PRICE_PRECISION = [
        'BTC-KES' => 2,
        'ETH-KES' => 2,
        'USDT-KES' => 4,
        'default' => 2
    ];

    // Quantity precision by market (decimal places)
    private const QUANTITY_PRECISION = [
        'BTC-KES' => 8,
        'ETH-KES' => 8,
        'USDT-KES' => 2,
        'default' => 8
    ];

    // Maximum order size
    private const MAX_ORDER_SIZE = 1000000; // 1M KES equivalent

    /**
     * Validate order parameters
     */
    public function validateOrder(array $orderData, string $userId): array
    {
        try {
            $market = Market::findOrFail($orderData['market_id']);
            
            // Validate market is active
            if (!$market->is_active) {
                throw ValidationException::withMessages([
                    'market_id' => ['Market is not active']
                ]);
            }

            // Validate order type
            $this->validateOrderType($orderData['type']);

            // Validate side
            $this->validateOrderSide($orderData['side']);

            // Validate price for limit orders
            if ($orderData['type'] === 'limit') {
                $this->validatePrice($orderData['price'], $market);
            }

            // Validate quantity
            $this->validateQuantity($orderData['quantity'], $market, $orderData['side']);

            // Validate minimum order size
            $this->validateMinimumOrderSize($orderData['quantity'], $orderData['price'], $market);

            // Validate maximum order size
            $this->validateMaximumOrderSize($orderData['quantity'], $orderData['price'], $market);

            // Validate user has sufficient funds
            if (isset($orderData['validate_funds']) && $orderData['validate_funds']) {
                $this->validateUserFunds($userId, $orderData['side'], $orderData['price'], $orderData['quantity'], $market);
            }

            // Validate price precision
            if (isset($orderData['price'])) {
                $this->validatePricePrecision($orderData['price'], $market);
            }

            // Validate quantity precision
            $this->validateQuantityPrecision($orderData['quantity'], $market);

            return [
                'valid' => true,
                'market' => $market,
                'validated_data' => $orderData
            ];

        } catch (\Exception $e) {
            Log::error('Order validation failed', [
                'user_id' => $userId,
                'order_data' => $orderData,
                'error' => $e->getMessage()
            ]);

            throw $e;
        }
    }

    /**
     * Validate order type
     */
    private function validateOrderType(string $type): void
    {
        $validTypes = ['limit', 'market', 'stop_loss', 'take_profit', 'iceberg'];
        
        if (!in_array($type, $validTypes)) {
            throw ValidationException::withMessages([
                'type' => ["Invalid order type: {$type}. Valid types are: " . implode(', ', $validTypes)]
            ]);
        }
    }

    /**
     * Validate order side
     */
    private function validateOrderSide(string $side): void
    {
        $validSides = ['buy', 'sell'];
        
        if (!in_array($side, $validSides)) {
            throw ValidationException::withMessages([
                'side' => ["Invalid order side: {$side}. Valid sides are: " . implode(', ', $validSides)]
            ]);
        }
    }

    /**
     * Validate price for limit orders
     */
    private function validatePrice(float $price, Market $market): void
    {
        if ($price <= 0) {
            throw ValidationException::withMessages([
                'price' => ['Price must be greater than zero']
            ]);
        }

        if ($price > $market->max_price) {
            throw ValidationException::withMessages([
                'price' => ["Price exceeds maximum allowed: {$market->max_price}"]
            ]);
        }

        if ($price < $market->min_price) {
            throw ValidationException::withMessages([
                'price' => ["Price is below minimum allowed: {$market->min_price}"]
            ]);
        }
    }

    /**
     * Validate quantity
     */
    private function validateQuantity(float $quantity, Market $market, string $side): void
    {
        if ($quantity <= 0) {
            throw ValidationException::withMessages([
                'quantity' => ['Quantity must be greater than zero']
            ]);
        }

        if ($quantity > $market->max_quantity) {
            throw ValidationException::withMessages([
                'quantity' => ["Quantity exceeds maximum allowed: {$market->max_quantity}"]
            ]);
        }

        if ($quantity < $market->min_quantity) {
            throw ValidationException::withMessages([
                'quantity' => ["Quantity is below minimum allowed: {$market->min_quantity}"]
            ]);
        }

        // Validate step size
        if ($market->step_size > 0) {
            $remainder = fmod($quantity, $market->step_size);
            if ($remainder > 0.00000001) { // Account for floating point precision
                throw ValidationException::withMessages([
                    'quantity' => ["Quantity must be a multiple of step size: {$market->step_size}"]
                ]);
            }
        }
    }

    /**
     * Validate minimum order size
     */
    private function validateMinimumOrderSize(float $quantity, ?float $price, Market $market): void
    {
        $minSize = $this->getMinOrderSize($market->name);
        $orderValue = $price ? $quantity * $price : $quantity;

        if ($orderValue < $minSize) {
            throw ValidationException::withMessages([
                'quantity' => ["Order value ({$orderValue}) is below minimum required: {$minSize}"]
            ]);
        }
    }

    /**
     * Validate maximum order size
     */
    private function validateMaximumOrderSize(float $quantity, ?float $price, Market $market): void
    {
        $orderValue = $price ? $quantity * $price : $quantity;

        if ($orderValue > self::MAX_ORDER_SIZE) {
            throw ValidationException::withMessages([
                'quantity' => ["Order value ({$orderValue}) exceeds maximum allowed: " . self::MAX_ORDER_SIZE]
            ]);
        }
    }

    /**
     * Validate user has sufficient funds
     */
    private function validateUserFunds(string $userId, string $side, float $price, float $quantity, Market $market): void
    {
        // This would integrate with the wallet system
        // For now, we'll just log that validation should occur
        Log::info('User funds validation needed', [
            'user_id' => $userId,
            'side' => $side,
            'price' => $price,
            'quantity' => $quantity,
            'market' => $market->name
        ]);
    }

    /**
     * Validate price precision
     */
    private function validatePricePrecision(float $price, Market $market): void
    {
        $precision = $this->getPricePrecision($market->name);
        $rounded = round($price, $precision);
        
        if (abs($price - $rounded) > 0.00000001) { // Account for floating point precision
            throw ValidationException::withMessages([
                'price' => ["Price must have maximum {$precision} decimal places"]
            ]);
        }
    }

    /**
     * Validate quantity precision
     */
    private function validateQuantityPrecision(float $quantity, Market $market): void
    {
        $precision = $this->getQuantityPrecision($market->name);
        $rounded = round($quantity, $precision);
        
        if (abs($quantity - $rounded) > 0.00000001) { // Account for floating point precision
            throw ValidationException::withMessages([
                'quantity' => ["Quantity must have maximum {$precision} decimal places"]
            ]);
        }
    }

    /**
     * Get minimum order size for market
     */
    private function getMinOrderSize(string $marketName): float
    {
        return self::MIN_ORDER_SIZES[$marketName] ?? self::MIN_ORDER_SIZES['default'];
    }

    /**
     * Get price precision for market
     */
    private function getPricePrecision(string $marketName): int
    {
        return self::PRICE_PRECISION[$marketName] ?? self::PRICE_PRECISION['default'];
    }

    /**
     * Get quantity precision for market
     */
    private function getQuantityPrecision(string $marketName): int
    {
        return self::QUANTITY_PRECISION[$marketName] ?? self::QUANTITY_PRECISION['default'];
    }

    /**
     * Validate advanced order types (stop-loss, take-profit, etc.)
     */
    public function validateAdvancedOrder(array $orderData, Market $market): void
    {
        switch ($orderData['type']) {
            case 'stop_loss':
                $this->validateStopLossOrder($orderData, $market);
                break;
                
            case 'take_profit':
                $this->validateTakeProfitOrder($orderData, $market);
                break;
                
            case 'iceberg':
                $this->validateIcebergOrder($orderData, $market);
                break;
        }
    }

    /**
     * Validate stop-loss order
     */
    private function validateStopLossOrder(array $orderData, Market $market): void
    {
        if (!isset($orderData['stop_price'])) {
            throw ValidationException::withMessages([
                'stop_price' => ['Stop price is required for stop-loss orders']
            ]);
        }

        if ($orderData['stop_price'] <= 0) {
            throw ValidationException::withMessages([
                'stop_price' => ['Stop price must be greater than zero']
            ]);
        }

        // Validate stop price logic
        if ($orderData['side'] === 'buy' && $orderData['stop_price'] <= $orderData['price']) {
            throw ValidationException::withMessages([
                'stop_price' => ['For buy stop-loss orders, stop price must be higher than limit price']
            ]);
        }

        if ($orderData['side'] === 'sell' && $orderData['stop_price'] >= $orderData['price']) {
            throw ValidationException::withMessages([
                'stop_price' => ['For sell stop-loss orders, stop price must be lower than limit price']
            ]);
        }
    }

    /**
     * Validate take-profit order
     */
    private function validateTakeProfitOrder(array $orderData, Market $market): void
    {
        if (!isset($orderData['stop_price'])) {
            throw ValidationException::withMessages([
                'stop_price' => ['Stop price is required for take-profit orders']
            ]);
        }

        // Validate take profit price logic
        if ($orderData['side'] === 'buy' && $orderData['stop_price'] >= $orderData['price']) {
            throw ValidationException::withMessages([
                'stop_price' => ['For buy take-profit orders, stop price must be lower than limit price']
            ]);
        }

        if ($orderData['side'] === 'sell' && $orderData['stop_price'] <= $orderData['price']) {
            throw ValidationException::withMessages([
                'stop_price' => ['For sell take-profit orders, stop price must be higher than limit price']
            ]);
        }
    }

    /**
     * Validate iceberg order
     */
    private function validateIcebergOrder(array $orderData, Market $market): void
    {
        if (!isset($orderData['visible_quantity'])) {
            throw ValidationException::withMessages([
                'visible_quantity' => ['Visible quantity is required for iceberg orders']
            ]);
        }

        if ($orderData['visible_quantity'] >= $orderData['quantity']) {
            throw ValidationException::withMessages([
                'visible_quantity' => ['Visible quantity must be less than total quantity']
            ]);
        }

        if ($orderData['visible_quantity'] <= 0) {
            throw ValidationException::withMessages([
                'visible_quantity' => ['Visible quantity must be greater than zero']
            ]);
        }
    }

    /**
     * Get validation rules for API documentation
     */
    public function getValidationRules(): array
    {
        return [
            'market_id' => 'required|exists:markets,id',
            'side' => 'required|in:buy,sell',
            'type' => 'required|in:limit,market,stop_loss,take_profit,iceberg',
            'quantity' => 'required|numeric|min:0.00000001',
            'price' => 'nullable|numeric|min:0.01',
            'stop_price' => 'nullable|numeric|min:0.01',
            'visible_quantity' => 'nullable|numeric|min:0.00000001',
            'time_in_force' => 'nullable|in:GTC,IOC,FOK'
        ];
    }
}