<?php
namespace App\Libraries;
/**
 * This is Api Class for Bybit on testnet as well as mainnet.
 * This class is based on V5 Api documentation in https://bybit-exchange.github.io/docs/v5/enum#unifiedmarginstatus
 * Created by : S Periyaraja
 * Created At : 17-07-2024
 */
class BybitApi 
{
	protected $base = 'https://api.bybit.com/'; // /< REST endpoint for the currency exchange
    protected $baseTestnet = 'https://api-testnet.bybit.com/'; // /< Testnet REST endpoint for the currency exchange
    protected $api_key; // /< API key that you created in the binance website member area
    protected $api_secret; // /< API secret that was given to you when you created the api key
    protected $useTestnet = false; // /< Enable/disable testnet (https://api-testnet.bybit.com/)
    protected $depthCache = []; // /< Websockets depth cache
    protected $depthQueue = []; // /< Websockets depth queue
    protected $chartQueue = []; // /< Websockets chart queue
    protected $charts = []; // /< Websockets chart data
    protected $curlOpts = []; // /< User defined curl coptions
    protected $info = [
        "timeOffset" => 0,
    ]; // /< Additional connection options
    protected $proxyConf = null; // /< Used for story the proxy configuration
    protected $caOverride = false; // /< set this if you donnot wish to use CA bundle auto download feature
    protected $transfered = 0; // /< This stores the amount of bytes transfered
    protected $requestCount = 0; // /< This stores the amount of API requests
    protected $httpDebug = false; // /< If you enable this, curl will output debugging information
    protected $subscriptions = []; // /< View all websocket subscriptions
    
    protected $exchangeInfo = null;
    protected $lastRequest = [];
    protected $baseUrl;

	
	public function __construct()
	{
        $this->useServerTime();
		 $param = func_get_args();
		 switch (count($param)) {
            case 2:
                $this->api_key = $param[0];
                $this->api_secret = $param[1];
                break;
            case 3:
                $this->api_key = $param[0];
                $this->api_secret = $param[1];
                $this->useTestnet = (bool)$param[2];
                break;
            default:
                echo 'Please Pass a API key and secret key in the BybitApi class';
        }

        $this->baseUrl = ($this->useTestnet) ? $this->baseTestnet : $this->base;
	}

	 public function useServerTime()
    {
        $request = $this->httpRequest("v5/market/time");
        if (isset($request['time'])) {
            $this->info['timeOffset'] = $request['time'] - (microtime(true) * 1000);
        }
    }

   	protected function httpRequest(string $endpoint, string $method = "GET", array $params = [])
    {
        if (function_exists('curl_init') === false) {
            throw new \Exception("Sorry cURL is not installed!");
        }
     


        $curl = curl_init();
        curl_setopt($curl, CURLOPT_VERBOSE, $this->httpDebug);
           if ($method == "GET") {
		    	$query_string = http_build_query($params, '', '&');
		        $endpoint = $endpoint . "?" . $query_string;
		    }else{
		    	$query_string = (!empty($params)) ? json_encode($params) : '';
		    }
		    
		 
	
         

	       $ts = (microtime(true) * 1000) + $this->info['timeOffset'];
	
         $timestamp = time() * 1000;
         $params_for_signature = $timestamp . $this->api_key . "5000" . $query_string;
		 $signature = hash_hmac('sha256', $params_for_signature, $this->api_secret);


		    $url = $this->getRestEndpoint();
		    
		
		    
		    
		       curl_setopt_array($curl, array(
			        CURLOPT_URL => $url . $endpoint,
			        CURLOPT_RETURNTRANSFER => true,
			        CURLOPT_ENCODING => '',
			        CURLOPT_MAXREDIRS => 10,
			        CURLOPT_TIMEOUT => 0,
			        CURLOPT_FOLLOWLOCATION => true,
			        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
			        CURLOPT_CUSTOMREQUEST => $method,
			        CURLOPT_HTTPHEADER => array(
			            "X-BAPI-API-KEY: $this->api_key",
			            "X-BAPI-SIGN: $signature",
			            "X-BAPI-SIGN-TYPE: 2",
			            "X-BAPI-TIMESTAMP: $timestamp",
			            "X-BAPI-RECV-WINDOW: 5000",
			            "Content-Type: application/json"
			        ),
			    ));

			    if ($method == "GET") {
			        curl_setopt($curl, CURLOPT_HTTPGET, true);
			    }else{
			    	curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($params));
			    }

			    $response = curl_exec($curl);
			    
			  

			       if ($response === false) {
				        $error_msg = curl_error($curl);
				        $error_info = curl_getinfo($curl);
				        $json = array(
				            'error' => true,
				            'message' => $error_msg,
				            'info' => $error_info
				        );
				    }else{    	
				   		$json = json_decode($response, true); 	
				    }
				    
				    
			    

			    $compacted = compact('url','method','params','json');
		         $this->lastRequest = $compacted;

	   return $json;

    }


    private function getRestEndpoint() : string
    {
        return $this->useTestnet ? $this->baseTestnet : $this->base;
    }

    public function getTotalBalance($symbol=false,$type = 'UNIFIED')
    {
    	$params = ['accountType'=>strtoupper($type)];
    	if ($symbol) {
    		$params['coin'] = strtoupper($symbol);
    	}
    	$request = $this->httpRequest('v5/account/wallet-balance','GET',$params);
    	$return = ['status' => false];
    	if (isset($request['retMsg']) && $request['retMsg'] == 'OK') {
    		$result = $request['result']['list'][0];
    		$coins = $result['coin'];
    		$return = [
    			'status' => true,
    			'totalAvailableBalance' => $result['totalAvailableBalance'],
    			'totalWalletBalance' => $result['totalWalletBalance']
    		];
    		if($coins && $symbol){
    			foreach ($coins as $row) {
    					if($row['coin'] == $symbol){
    						$return['coin_balance'] = $row['walletBalance'];
    						break;
    					}
    			}
    		}else{
    			$return['coins'] = $coins;
    		}




    	}else{
    		$return['response'] = $request;
    	}
    	return $return;
    }


     public function getBalance($symbol = 'USDT',$type = 'UNIFIED')
    {
    	$params = ['accountType'=>strtoupper($type)];
    	if ($symbol) {
    		$params['coin'] = strtoupper($symbol);
    	}
    	$balance = 0;
    	$request = $this->httpRequest('v5/asset/transfer/query-account-coins-balance','GET',$params);

    	if (isset($request['result'])) {
    		$result = $request['result'];
    		$coins = $result['balance'];
    
    		if($coins){
    			foreach ($coins as $row) {
    					if($row['coin'] == $symbol){
    						$balance = $row['transferBalance'];
    						break;
    					}
    			}
    		}
    	}
    	return $balance;
    }
    
    //   public function getLivePrice($symbol)
    // {
        
    //      $params = [
    //         'symbol' => $symbol // e.g., 'BTCUSDT' for BTC/USDT pair
    //     ];
        
    //     $request = $this->httpRequest('v5/asset/transfer/query-account-coins-balance','GET',$params);
        
        
   // }
     
    public function getBalance1($symbol,$type = 'UNIFIED')
    {
    	$params = ['accountType'=>strtoupper($type)];
    	if ($symbol) {
    		$params['coin'] = strtoupper($symbol);
    	}
    	$balance = 0;
    	$request = $this->httpRequest('v5/asset/transfer/query-account-coins-balance','GET',$params);

    	if (isset($request['result'])) {
    		$result = $request['result'];
    		$coins = $result['balance'];
    
    		if($coins){
    			foreach ($coins as $row) {
    					if($row['coin'] == $symbol){
    					    
    					    //print_r($row);
    					    
    						$balance = $row['transferBalance'];
    						break;
    					}
    			}
    		}
    	}
    	return $balance;
    }
/**
 * Category - 'spot' , 'linear' , 'inverse'
 * symbol - 'BTCUSDT' must be in caps
 * side - 'Buy','Sell'
 * orderType - 'Market' , 'Limit'
 * qty - quantity 
 * price - can be 0 if market order else must be set 
*/
    public function order($category,$symbol,$side,$orderType,$qty, $price = 0 ,array $other_params = [],$positionIdx = null)
    {
        
  
        $opt = compact('category','symbol','side','orderType','qty');

        // someone has preformated there 8 decimal point double already
        // dont do anything, leave them do whatever they want
        if (gettype($price) !== "string") {
            // for every other type, lets format it appropriately
            $price = number_format($price, 8, '.', '');
        }

        if (is_numeric($qty) === false) {
            // WPCS: XSS OK.
            echo "warning: quantity expected numeric got " . gettype($qty) . PHP_EOL;
        }

        if (is_string($price) === false) {
            // WPCS: XSS OK.
            echo "warning: price expected string got " . gettype($price) . PHP_EOL;
        }

        if ($orderType === "LIMIT") {
            $opt["price"] = $price;
            $opt["timeInForce"] = "GTC";
        }
        
        // Add positionIdx if provided and not null
    if ($positionIdx !== null) {
        $opt['positionIdx'] = $positionIdx;
    }
        

        if(!empty($other_params)){
        	$opt = $this->add_assoc($opt,$other_params);
        }
        // ksort($opt);

        $qstring =  "v5/order/create" ;
        return $this->httpRequest($qstring, "POST", $opt);
    }
    
    public function stoporder($symbol)
    {
         $take_profit = 0.007; // Set a take-profit price
        $stop_loss = 0.007; // Set a stop-loss price
        $trailing_stop = 0; 
        $symbol='BOMEUSDT';
        $positionIdx='1';
        
        /*$params = [
        "symbol" => $symbol,
        "category" => 'linear', 
        "position_idx" => $positionIdx,
        "take_profit" => $take_profit,
        "stop_loss" => $stop_loss,
        "trailing_stop" => $trailing_stop
        ];*/
        
        /*$params = [
        "symbol" => $symbol,
        "category" => 'linear', 
        "orderId" => 'f29ff6c5-a6ee-430b-9f7e-a7c5fd139859'
        
        ];*/
        
        $params = [
            "category" =>"linear",
             "symbol" =>"BOMEUSDT",
            "takeProfit"=>"0.007293",
            "stopLoss"=> "0.007293",
              "tpSize" => null,
              "slSize"=> null,
              "tpTriggerBy" => "MarkPrice",
              "slTriggerBy"=> "MarkPrice",
              "trailingStop" => null,
              "activePrice"=> null,
              "positionIdx" => 1,
              "tpLimitPrice" => null,
              "slLimitPrice" => null,
              "tpOrderType" => null,
              "slOrderType" => null
        ];
        
        
        
        
        
        
        
        $qstring =  "v5/position/trading-stop";
        return $this->httpRequest($qstring, "POST", $params);
        
    }


    public function GetOpenOrders($opt)
    {
    	
         return $this->httpRequest('v5/order/realtime', "GET", $opt);
    }

    private function add_assoc($old,$new)
    {
    	foreach ($new as $key => $value) {
    		$old[$key] = $value;
    	}
    	return $old;
    }

    public function GetOrders($opt)
    {

    	return $this->httpRequest('v5/order/history', "GET", $opt);
    }
/**
 * In accountinfo return unifiedMarginStatus
 * if unifiedMarginStatus
 * 1 => NORMAL or CLASSIC account
 * 3 => UNIFIED account
 * 4  => UNIFIED_PRO account
*/
    public function accountInfo()
    {
    	$response = $this->httpRequest('v5/account/info');
    
    	$result = isset($response['result']) && !empty($response['result']) ? $response['result'] : false;
    	return $result;
    }

    public function check_endpoint(string $endpoint, string $method = "GET", array $params = [])
    {
    	return $this->httpRequest($endpoint, $method, $params);
    }
    /**
     * Need category and baseCoin as param
     * category => linear
     * baseCoin => USDT
     * */
    public function ticker($params)
    {
      
    	$request = $this->httpRequest('v5/market/tickers', 'GET', $params);


    if(isset($params['symbol'])){
         $lists = (isset($request['result']) && !empty($request['result'])) ? $request['result']['list'][0] : false;           
     }else{
        $lists = (isset($request['result']) && !empty($request['result'])) ? $request['result']['list'] : false;
     }


         return $lists;
    }


     public function getMarketInfo($params)
    {
        $request = $this->httpRequest('v5/market/instruments-info', 'GET', $params);


    if(isset($params['symbol'])){
         $lists = (isset($request['result']) && !empty($request['result'])) ? $request['result']['list'][0] : false;           
     }else{
        $lists = (isset($request['result']) && !empty($request['result'])) ? $request['result']['list'] : false;
     }


         return $lists;
    }


     public function getCoinBalance($baseCoin = false)
    {

        if($baseCoin){
            $request = $this->httpRequest('v5/asset/coin-greeks', 'GET', compact('baseCoin'));
            $lists = (isset($request['result']) && !empty($request['result'])) ? $request['result']['list'][0] : false;       
        }else{
            $request = $this->httpRequest('v5/asset/coin-greeks', 'GET');
 
             $lists = (isset($request['result']) && !empty($request['result'])) ? $request['result']['list'] : false;
        }
        


         return $lists;
    }
    
  

    
    public function get_timestamp()
    {
        $ch = curl_init();
        // Set the URL and other cURL options
        curl_setopt($ch, CURLOPT_URL, "https://api.bybit.com/v5/time");
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        // Execute the request and get the response
        $response = curl_exec($ch);
        // Close the cURL session
        curl_close($ch);
        // Decode the JSON response
        $serverTime = json_decode($response, true);
        //return $serverTime['serverTime'];
    
    }




}