AlkantarClanX12

Your IP : 216.73.217.24


Current Path : /www/capitalgmcbuickregina_830/public/wp-content/plugins/leadbox/classes/
Upload File :
Current File : /www/capitalgmcbuickregina_830/public/wp-content/plugins/leadbox/classes/api.php

<?php if (!defined('ABSPATH'))
  exit;

final class LB_API_Client
{

  private $apiUrl;
  private $apiKey;
  private $apiLeadKey;
  private $usecache;

  public function __construct($apiUrl, $apiKey, $apiLeadKey, $usecache)
  {
    $this->apiUrl = $apiUrl;
    $this->apiKey = $apiKey;
    $this->apiLeadKey = $apiLeadKey;
    $this->usecache = $usecache;
  }

  public static function getInstance()
  {
    $settings = LB_API_Settings::getInstance();
    return new LB_API_Client(
      $settings->getApiUrl(),
      $settings->getApiKey(),
      $settings->getApiLeadKey(),
      $settings->getApiCache()
    );
  }

  /**
   * Create a standardized API response
   *
   * @param bool $success Whether the operation succeeded
   * @param mixed $data The response data (or null on error)
   * @param string|null $message User-friendly message
   * @param string|null $error_code Machine-readable error code
   * @return array Standardized response array
   */
  private function create_response($success, $data, $message = null, $error_code = null)
  {
    return array(
      'success' => $success,
      'data' => $data,
      'count' => is_array($data) ? count($data) : 0,
      'message' => $message,
      'error_code' => $error_code,
      'timestamp' => time()
    );
  }

  public function CreateLead($lead)
  {
    if (FALSE == ($lead instanceof LB_Lead_Dto)) {
      throw new Exception('$lead needs to be instance of Lead Object');
    }

    $useLeadKey = !empty($this->apiLeadKey);

    $result = $this->POSTAsync('lead', $lead->ToArray(), $useLeadKey);

    // POSTAsync returns error string on failure
    if (is_string($result)) {
      return $this->create_response(
        false,
        null,
        'Unable to submit lead. Please try again later.',
        'LEAD_SUBMISSION_FAILED'
      );
    }

    // If result is array with success key (structured response), pass it through
    if (is_array($result) && isset($result['success'])) {
      return $result;
    }

    // Success - return the result from POSTAsync
    return $this->create_response(
      true,
      $result,
      'Lead submitted successfully',
      null
    );
  }

  public function VehicleSearch($parameters)
  {
    $result = $this->GET('vehicle/search', $parameters, 10);

    // If GET already returned a structured error response, pass it through
    if (is_array($result) && isset($result['success']) && !$result['success']) {
      return $result;
    }

    // Validate response structure for vehicle search
    if (!is_object($result) && !is_array($result)) {
      return $this->create_response(
        false,
        null,
        'Received unexpected data format from vehicle database.',
        'API_INVALID_RESPONSE'
      );
    }

    // Convert object to array for consistency
    if (is_object($result)) {
      $result = json_decode(json_encode($result), true);
    }

    // Check if we have vehicles in the response
    $vehicles = isset($result['vehicles']) ? $result['vehicles'] : $result;

    // Success with results or empty results
    return $this->create_response(
      true,
      $vehicles,
      empty($vehicles) ? 'No vehicles match your search criteria' : null,
      null
    );
  }

  public function VehicleCustomSearch($parameters)
  {
    $result = $this->GET('vehicle/customsearch', $parameters, 10);

    // If GET already returned a structured error response, pass it through
    if (is_array($result) && isset($result['success']) && !$result['success']) {
      return $result;
    }

    // Validate response structure
    if (!is_object($result) && !is_array($result)) {
      return $this->create_response(
        false,
        null,
        'Received unexpected data format from vehicle database.',
        'API_INVALID_RESPONSE'
      );
    }

    // Convert object to array for consistency
    if (is_object($result)) {
      $result = json_decode(json_encode($result), true);
    }

    // Check if we have vehicles in the response
    $vehicles = isset($result['vehicles']) ? $result['vehicles'] : $result;

    // Success with results or empty results
    return $this->create_response(
      true,
      $vehicles,
      empty($vehicles) ? 'No vehicles match your search criteria' : null,
      null
    );
  }

  public function VehicleDetail($id)
  {
    if (FALSE == is_numeric($id)) {
      throw new Exception('id is not a valid number');
    }

    $result = $this->GET('vehicle/' . $id, FALSE, 15);

    // If GET already returned a structured error response, pass it through
    if (is_array($result) && isset($result['success']) && !$result['success']) {
      return $result;
    }

    // Validate response structure
    if (!is_object($result) && !is_array($result)) {
      return $this->create_response(
        false,
        null,
        'Vehicle not found.',
        'API_INVALID_RESPONSE'
      );
    }

    // Success - return vehicle details
    return $this->create_response(
      true,
      $result,
      null,
      null
    );
  }

  public function VehicleSimilars($id)
  {
    if (FALSE == is_numeric($id)) {
      throw new Exception('id is not a valid number');
    }

    $result = $this->GET('vehicle/' . $id . '/similars', FALSE, 60);

    // If GET already returned a structured error response, pass it through
    if (is_array($result) && isset($result['success']) && !$result['success']) {
      return $result;
    }

    // Validate response structure
    if (!is_object($result) && !is_array($result)) {
      return $this->create_response(
        false,
        null,
        'Unable to find similar vehicles.',
        'API_INVALID_RESPONSE'
      );
    }

    // Convert object to array for consistency
    if (is_object($result)) {
      $result = json_decode(json_encode($result), true);
    }

    // Success with results or empty results
    return $this->create_response(
      true,
      $result,
      empty($result) ? 'No similar vehicles found' : null,
      null
    );
  }

  public function VehicleMatches($parameters)
  {
    return $this->GET('vehicle/search/totalmatches', $parameters, 10);
  }

  public function AvailableYears($parameters = FALSE)
  {
    return $this->GET('resource/year', $parameters, 60 * 6);
  }

  public function AvailableMakes($parameters = FALSE)
  {
    return $this->GET('resource/make', $parameters, 60 * 6);
  }

  public function AvailableModels($parameters = FALSE)
  {
    return $this->GET('resource/model', $parameters, 60 * 6);
  }

  public function AvailableTransmissions($parameters = FALSE)
  {
    return $this->GET('resource/transmission', $parameters, 60 * 6);
  }

  public function AvailableTypes($parameters = FALSE)
  {
    return $this->GET('resource/type', $parameters, 60 * 6);
  }

  public function AvailableColors($parameters = FALSE)
  {
    return $this->GET('resource/color', $parameters, 60 * 6);
  }

  public function AvailableEngineCyliders($parameters = FALSE)
  {
    return $this->GET('resource/enginecylinders', $parameters, 60 * 6);
  }

  public function AvailableDriveTrain($parameters = FALSE)
  {
    return $this->GET('resource/drivetrain', $parameters, 60 * 6);
  }

  public function AvailableEngineDisplacement($parameters = FALSE)
  {
    return $this->GET('resource/enginedisplacement', $parameters, 60 * 6);
  }

  public function AvailableCab($parameters = FALSE)
  {
    return $this->GET('resource/cab', $parameters, 60 * 6);
  }

  public function AvailableFeatures($parameters = FALSE)
  {
    return $this->GET('resource/category/full', $parameters, 60 * 6);
  }

  public function AvailableLocations($parameters = FALSE)
  {
    return $this->GET('resource/location', $parameters, 60 * 6);
  }

  public function AvailableConditions($parameters = FALSE)
  {
    return $this->GET('resource/condition', $parameters, 60 * 6);
  }

  public function Locations()
  {
    return $this->GET('resource/location/info/all', FALSE, 60 * 6);
  }

  public function LocationInfo($name)
  {
    if (empty($name)) {
      throw new Exception('Location name cannot be empty');
    }

    return $this->GET('resource/location/info?name=' . $name, FALSE, 60 * 6);
  }

  public function SaveVisitorVehicle($email, $carId)
  {
    if (FALSE == is_numeric($carId)) {
      throw new Exception('id is not a valid number');
    }

    $SaveVisitor = array();
    $SaveVisitor["email"] = $email;
    $SaveVisitor["vehicleId"] = $carId;

    return $this->POST('visitors', $SaveVisitor);
  }

  public function SaveVisitorVehiclesIds($email, $Ids)
  {

    $SaveVisitorIds = array();
    $SaveVisitorIds["email"] = $email;
    $SaveVisitorIds['vehicleIds'] = $Ids;

    return $this->POST('visitors', $SaveVisitorIds);
  }

  public function GetVisitorVehicles($email)
  {
    return $this->GET('visitors?email=' . $email, false, false);
  }

  public function GetVisitorVehiclesFull($email)
  {
    return $this->GET('visitors/full?email=' . $email, false, false);
  }

  public function DeleteVisitorVehicle($email, $carId)
  {

    $Deletevisitor = array();
    $Deletevisitor["email"] = $email;
    $Deletevisitor["vehicleId"] = $carId;

    return $this->DELETE('visitors', $Deletevisitor);
  }

  private function GET($url, $parameters = FALSE, $cachetime = 5, $useLeadKey = FALSE)
  {

    if (empty($url)) {
      throw new Exception('$url cannot be empty');
    }

    if ($parameters != FALSE) {

      if (FALSE == ($parameters instanceof SearchParameters)) {
        throw new Exception('$parameters needs to be instance of SearchParameters Object');
      }

      $aParameters = $parameters->ToArray();

      foreach ($aParameters as $key => $value) {
        if (is_bool($value)) {
          $aParameters[$key] = ($value) ? 'true' : 'false';
        }
      }

      $endpoint = $this->apiUrl . '/api/v1/' . $url . '?' . http_build_query($aParameters);
    } else {
      $endpoint = $this->apiUrl . '/api/v1/' . $url;
    }

    $cache_key = md5($endpoint);
    $cached = get_transient($cache_key);

    // Return cached data if available
    if ($this->usecache && $cached !== false) {
      return $cached;
    }

    $args = array(
      'timeout' => 120,
      'headers' => array(
        'Authorization' => $useLeadKey ? $this->apiLeadKey : $this->apiKey
      )
    );

    $result = wp_remote_get($endpoint, $args);

    // Connection error
    if (is_wp_error($result)) {
      $error_message = 'Unable to connect to vehicle database. Please try again later.';

      if (defined('WP_DEBUG') && WP_DEBUG) {
        $error_message .= ' (' . $result->get_error_message() . ')';
      }

      // Log error if logger is available
      if (function_exists('leadbox_logger')) {
        leadbox_logger()->error('API Connection Failed', array(
          'endpoint' => $endpoint,
          'error' => $result->get_error_message()
        ));
      }

      $response = $this->create_response(
        false,
        null,
        $error_message,
        'API_CONNECTION_FAILED'
      );

      // Try to return stale cache if available
      $stale_cache = get_transient($cache_key . '_stale');
      if ($stale_cache !== false) {
        $response['data'] = $stale_cache;
        $response['count'] = is_array($stale_cache) ? count($stale_cache) : 0;
        $response['message'] = 'Showing cached results. Live data temporarily unavailable.';
        $response['error_code'] = 'USING_STALE_CACHE';
      }

      return $response;
    }

    // HTTP error status
    $status_code = wp_remote_retrieve_response_code($result);
    if ($status_code !== 200) {
      $error_message = 'Vehicle database returned an error. Please try again later.';

      if (function_exists('leadbox_logger')) {
        leadbox_logger()->error('API HTTP Error', array(
          'endpoint' => $endpoint,
          'status_code' => $status_code
        ));
      }

      return $this->create_response(
        false,
        null,
        $error_message,
        'API_HTTP_ERROR_' . $status_code
      );
    }

    // Parse response
    $body = wp_remote_retrieve_body($result);
    $data = json_decode($body, true);

    // JSON parse error
    if (json_last_error() !== JSON_ERROR_NONE) {
      if (function_exists('leadbox_logger')) {
        leadbox_logger()->error('API Parse Error', array(
          'endpoint' => $endpoint,
          'json_error' => json_last_error_msg()
        ));
      }

      return $this->create_response(
        false,
        null,
        'Received invalid data from vehicle database.',
        'API_PARSE_ERROR'
      );
    }

    // Validate that we got some data
    if (!is_array($data) && !is_object($data)) {
      if (function_exists('leadbox_logger')) {
        leadbox_logger()->error('API Invalid Response', array(
          'endpoint' => $endpoint,
          'data_type' => gettype($data)
        ));
      }

      return $this->create_response(
        false,
        null,
        'Received unexpected data format from vehicle database.',
        'API_INVALID_RESPONSE'
      );
    }

    // Success! Cache the raw data as before for backwards compatibility
    if ($this->usecache && $data) {
      set_transient($cache_key, $data, 60 * $cachetime);

      // Also save as stale cache (longer TTL for fallback)
      set_transient($cache_key . '_stale', $data, 24 * HOUR_IN_SECONDS);
    }

    return $data;
  }

  private function POST($url, $payload, $useLeadKey = FALSE)
  {

    if (FALSE == is_array($payload)) {
      throw new Exception('$payload needs to be array object');
    }

    $args = array(
      'timeout' => 120,
      'headers' => array(
        'Authorization' => $useLeadKey ? $this->apiLeadKey : $this->apiKey
      ),
      'body' => $payload
    );

    $endpoint = $this->apiUrl . '/api/v1/' . $url;

    $result = wp_remote_post($endpoint, $args);

    // Connection error
    if (is_wp_error($result)) {
      $error_message = 'Unable to connect to server. Please try again later.';

      if (defined('WP_DEBUG') && WP_DEBUG) {
        $error_message .= ' (' . $result->get_error_message() . ')';
      }

      // Log error if logger is available
      if (function_exists('leadbox_logger')) {
        leadbox_logger()->error('API POST Connection Failed', array(
          'endpoint' => $endpoint,
          'error' => $result->get_error_message()
        ));
      }

      return $this->create_response(
        false,
        null,
        $error_message,
        'API_CONNECTION_FAILED'
      );
    }

    // HTTP error status
    $status_code = wp_remote_retrieve_response_code($result);
    if ($status_code < 200 || $status_code >= 300) {
      $error_message = 'Server returned an error. Please try again later.';

      if (function_exists('leadbox_logger')) {
        leadbox_logger()->error('API POST HTTP Error', array(
          'endpoint' => $endpoint,
          'status_code' => $status_code
        ));
      }

      return $this->create_response(
        false,
        null,
        $error_message,
        'API_HTTP_ERROR_' . $status_code
      );
    }

    // Parse response
    $body = wp_remote_retrieve_body($result);
    $data = json_decode($body, true);

    // JSON parse error
    if (json_last_error() !== JSON_ERROR_NONE) {
      if (function_exists('leadbox_logger')) {
        leadbox_logger()->error('API POST Parse Error', array(
          'endpoint' => $endpoint,
          'json_error' => json_last_error_msg()
        ));
      }

      return $this->create_response(
        false,
        null,
        'Received invalid data from server.',
        'API_PARSE_ERROR'
      );
    }

    // Success!
    return $data;
  }

  private function GetCurlSession($url, $payload, $key)
  {
    $headers = array(
      'Content-Type:application/json',
      'Accept:application/json',
      'Authorization:' . $key
    );

    $endpoint = $this->apiUrl . '/api/v1/' . $url;
    $body = json_encode($payload);
    $session = curl_init($endpoint);

    curl_setopt($session, CURLOPT_POST, true);
    curl_setopt($session, CURLOPT_POSTFIELDS, $body);
    curl_setopt($session, CURLOPT_HEADER, false);
    curl_setopt($session, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($session, CURLOPT_TIMEOUT_MS, 5000);

    // Devolver el manejador cURL
    return $session;
  }

  private function POSTAsync($url, $payload, $useLeadKey = false)
  {
    if (!is_array($payload)) {
      throw new Exception('$payload needs to be an array object');
    }

    $authorizationKey = $useLeadKey ? $this->apiLeadKey : $this->apiKey;

    try {
      // Obtener el manejador cURL sin ejecutar la petición
      $session = $this->GetCurlSession($url, $payload, $authorizationKey, true);

      if (!($session instanceof CurlHandle)) {
        throw new Exception('GetCurlSession() did not return a valid cURL handle');
      }

      $mh = curl_multi_init();
      curl_multi_add_handle($mh, $session);

      // Ejecutar multi-cURL
      $active = null;
      do {
        $status = curl_multi_exec($mh, $active);
        if ($active) {
          curl_multi_select($mh);
        }
      } while ($active && $status == CURLM_OK);

      // Obtener la respuesta y código HTTP
      $response = curl_multi_getcontent($session);
      $http_code = curl_getinfo($session, CURLINFO_HTTP_CODE);

      // Verificar si hubo error en cURL
      if (curl_errno($session)) {
        $error_msg = curl_error($session);
        leadbox_logger()->error('Backend connection failed', array(
          'error' => $error_msg,
          'curl_errno' => curl_errno($session),
          'endpoint' => $url,
        ));
        if (curl_errno($session) == CURLE_OPERATION_TIMEOUTED) {
          // Timeout específico
          throw new Exception('Timeout while connecting to the backend');
        }
        throw new Exception('Backend not available: ' . $error_msg);
      }

      curl_multi_remove_handle($mh, $session);
      curl_multi_close($mh);

      // Validación del backend
      if ($http_code !== 200) {
        leadbox_logger()->error('Backend returned non-200 status', array(
          'http_code' => $http_code,
          'endpoint' => $url,
        ));
        throw new Exception('Backend not available: HTTP Code ' . $http_code);
      }

      return json_decode($response, true); // Retornar respuesta si es válida

    } catch (Exception $e) {
      leadbox_logger()->error('POSTAsync failed', array(
        'error' => $e->getMessage(),
        'endpoint' => $url,
        'trace' => $e->getTraceAsString(),
      ));
      return $e->getMessage();
    }
  }

  private function DELETE($url, $payload)
  {

    if (FALSE == is_array($payload)) {
      throw new Exception('$payload needs to be array object');
    }

    $args = array(
      'method' => 'DELETE',
      'timeout' => 120,
      'headers' => array(
        'Authorization' => $this->apiKey
      ),
      'body' => $payload
    );

    $endpoint = $this->apiUrl . '/api/v1/' . $url;
    $result = wp_remote_request($endpoint, $args);

    // Connection error
    if (is_wp_error($result)) {
      $error_message = 'Unable to connect to server. Please try again later.';

      if (defined('WP_DEBUG') && WP_DEBUG) {
        $error_message .= ' (' . $result->get_error_message() . ')';
      }

      // Log error if logger is available
      if (function_exists('leadbox_logger')) {
        leadbox_logger()->error('API DELETE Connection Failed', array(
          'endpoint' => $endpoint,
          'error' => $result->get_error_message()
        ));
      }

      return $this->create_response(
        false,
        null,
        $error_message,
        'API_CONNECTION_FAILED'
      );
    }

    // HTTP error status
    $status_code = wp_remote_retrieve_response_code($result);
    if ($status_code < 200 || $status_code >= 300) {
      $error_message = 'Server returned an error. Please try again later.';

      if (function_exists('leadbox_logger')) {
        leadbox_logger()->error('API DELETE HTTP Error', array(
          'endpoint' => $endpoint,
          'status_code' => $status_code
        ));
      }

      return $this->create_response(
        false,
        null,
        $error_message,
        'API_HTTP_ERROR_' . $status_code
      );
    }

    // Parse response
    $body = wp_remote_retrieve_body($result);
    $data = json_decode($body, true);

    // JSON parse error
    if (json_last_error() !== JSON_ERROR_NONE) {
      if (function_exists('leadbox_logger')) {
        leadbox_logger()->error('API DELETE Parse Error', array(
          'endpoint' => $endpoint,
          'json_error' => json_last_error_msg()
        ));
      }

      return $this->create_response(
        false,
        null,
        'Received invalid data from server.',
        'API_PARSE_ERROR'
      );
    }

    // Success!
    return $data;
  }
}
?>