<?php

require_once('../trips/Quotes.php');

use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\Exception\UnsatisfiedDependencyException;

class AutocabApi extends Api {

	public $apiName = 'autocab';

	protected function indexAction() {
    global $autocabApiConfig;

    $code = 400;
    $message = "Bad request";
    $data = [];

    $memberId = $this->requestParams["member_id"] ?? null;
    $params = [
      'status' => $this->requestParams["status"] ?? null,
      'active' => $this->requestParams["active"] ?? false,
      'limit' => $this->requestParams["limit"] ?? 10,
      'offset' => $this->requestParams["offset"] ?? 0,
    ];

    $db = new Db();

    $sessionId = null;
    $requestHeaders = getallheaders();
    if ((empty($memberId) ||  $memberId <=0 ) && array_key_exists("x-session-id",$requestHeaders)) {
      $sessionId = $requestHeaders["x-session-id"];
      list($member, $error) = Booking::getMemberBySessionId($db->getConnect(), $sessionId);
      if (isset($member)) {
        $memberId = $member['id'];
      }
    }

    if (isset($memberId)) {
      list($message,$code,$action,$data) = AutocabApi::memberBookingList($db, $memberId, $params);
    } else {
      list($message,$code,$action,$data) =  AutocabApi::bookingList($db, $params);
    }

    if ($code==200) {
      $log = [
        'message' => $message,
        'code'    => $code,
        'action'  => $action,
        'data'    => $data
      ];
      Logger::warning($log);
      if ($action=="response") {
        return $this->response($data, $code);
      }
    }

    return $this->response(
      array(
        "error" => $message
      ), $code
    );
	}

	public function viewAction() {
		global $autocabApiConfig;

    $code = 400;
    $message = "Bad request";
    $data = [];

		$action = array_shift($this->requestUri);

		if (is_numeric($action)) {
      $id = $action;
      $action = 'booking';
    } else {
		  $id = array_shift($this->requestUri);
    }

    $db = new Db();
    $autocab = new Autocab($autocabApiConfig);

    switch($action) {
      case 'status':
        list($message,$code,$action,$data) = AutocabApi::bookingStatus($autocab, $db, $id);
        break;
      case 'member':
        $requestHeaders = getallheaders();
        if ((empty($id) ||  $id <=0 ) && array_key_exists("x-session-id",$requestHeaders)) {
          $sessionId = $requestHeaders["x-session-id"];
          list($member, $error) = Booking::getMemberBySessionId($db->getConnect(), $sessionId);
          if (isset($member)) {
            $id = $member['id'];
          }
        }
        if(!empty($id)) {
          list($message,$code,$action,$data) = AutocabApi::memberBookingList($db, $id);
        }
        break;
      case 'booking':
        list($message,$code,$action,$data) = AutocabApi::bookingInfo($db, $id);
        break;

    }

    if ($code==200) {
      $log = [
        'message' => $message,
        'code'    => $code,
        'action'  => $action,
        'data'    => $data
      ];
      Logger::warning($log);
      if ($action=="response") {
        return $this->response($data, $code);
      }
    }
    return $this->response(
      array(
        "error" => $message
      ), $code);
	}

	public function createAction() {

		global $autocabApiConfig, $savvyext;

    $action = array_shift($this->requestUri);

		Logger::debug(['message' => 'AutocabApi::createAction()']);

		$code = 400;
		$message = "Bad request";
		$data = [];

		$quoteId = $this->requestParams["quote_id"] ?? null;
		$bookingId = $this->requestParams["booking_id"] ?? null;
		$memberId = $this->requestParams["member_id"] ?? 0;

		$autocab = new Autocab($autocabApiConfig);
		$db = new Db();


    if ($action == 'check' || $action =='booking') {
      $requestHeaders = getallheaders();
      if ((empty($memberId) ||  $memberId <=0 ) && array_key_exists("x-session-id",$requestHeaders)) {
        $sessionId = $requestHeaders["x-session-id"];
        list($member, $error) = Booking::getMemberBySessionId($db->getConnect(), $sessionId);
        if (isset($member)) {
          $memberId = $member['id'];
        }
      }
      if ((empty($memberId) ||  $memberId <=0 )) {
          $message = "Couldn't identify Member Id. The member_id or x-session-id is not specified";
          return $this->response(
            array(
              "error" => $message
            ), $code
          );
      }
    }


		switch($action) {
      case "check":
        if (isset($quoteId)) {
          list($message,$code,$action,$data) = AutocabApi::checkOffer($autocab, $db, $quoteId, $memberId, $bookingId);
        }
        break;
      case "booking":
        if(isset($bookingId)) {
          list($message,$code,$action,$data) = AutocabApi::booking($autocab, $db, $bookingId, $memberId);
        }
        break;
      case "events":
        list($message,$code,$action,$data) = AutocabApi::eventHandler($autocab, $db, $this->requestParams);
        break;
      default:
        if (isset($quoteId)) {
          list($message,$code,$action,$data) = AutocabApi::checkOffer($autocab, $db, $quoteId, $memberId, $bookingId);
        } else if(isset($bookingId)) {
          list($message,$code,$action,$data) = AutocabApi::booking($autocab, $db, $bookingId, $memberId);
        }
    }


		if ($code==200) {
			$log = [
				'message' => $message,
				'code'    => $code,
				'action'  => $action,
				'data'    => $data
			];
			Logger::warning($log);
			if ($action=="response") {
				return $this->response($data, $code);
			}
		}
		return $this->response(
			array(
				"error" => $message
			), $code);
	}

	public function updateAction() {
		global $autocabApiConfig;
		$message = 'Update error';
		$code = 400;

		$bookingId = $this->requestParams["booking_id"];
		$vendorId = $this->requestParams["vendor_id"] ?? null;
		$availabilityRef = $this->requestParams["availability_ref"] ?? null;

		try {
			$db = new Db();
			$autocab = new Autocab($autocabApiConfig);

			if(isset($bookingId)) {
				$booking = Booking::getById($db->getConnect(), $bookingId);
				if (count($booking) == 0) {
					throw new Exception( "Booking info not found for id = ${bookingId}", 400);
				}
				$details = json_decode($booking["details"], true);
				$vendorId = $details["vendor_id"];
				$availabilityRef = $details["availability_ref"];
			} else if (isset($availabilityRef)) {
				$booking = Booking::getByBookingRef($db->getConnect(), $bookingId);
				if (count($booking) == 0) {
					throw new Exception( "Booking info not found for booking ref = ${availabilityRef}", 400);
				}
				$bookingId = $booking["id"];
				$details = json_decode($booking["details"], true);
				$vendorId = $details["vendor_id"];
				$availabilityRef = $details["availability_ref"];
			} else {
				throw new Exception("Required parameters booking_id or availability_ref is not provided", 400);
			}

			if (isset($availabilityRef)) {
				$resp = $autocab->cancelAvailability($availabilityRef, $vendorId);
				$error = $resp["error"];
				$data = $resp["data"];
			} else {
				throw new Exception("Invalid parameters set", 400);
			}

			if ($error != 0) {
				$message = $data["error"].": ".$data["error_description"].(isset($data["error_code"]) ? " Code:".$data["error_code"]:"");
				if (isset($bookingId)) {
					Booking::update($db->getConnect(), [
						"id"=>$bookingId,
						"message" => $message,
						"completed" => "now()",
						"updated" => "now()",
	          "status" => Booking::STATUS_ERROR,
					]);
				}
				throw new Exception($message);
			}
			if (isset($bookingId)) {
				Booking::update( $db->getConnect(), [
					"id"        => $booking["id"],
					"completed" => "now()",
					"updated"   => "now()",
					"status"    => 3,
					"message" => "Canceled"
				] );
			}
      $rawData = $resp["raw_data"];
      $request = $resp["request"];
      AutocabAPI::storeDetails($db, $booking["id"], "cancel", $rawData, $booking["message"], $request);
    } catch (Exception $e) {
			$message = $e->getMessage();
			$code = $e->getCode() != 0 ? $e->getCode() : 500;
			return $this->response( [	"error" => $message ], $code);
		}
		return $this->response([], 200);
	}

	public function deleteAction() {
		global $autocabApiConfig;
		$message = 'Delete error';
		$code = 400;

		$bookingId = array_shift($this->requestUri);

		try {
			$db = new Db();
			$autocab = new Autocab($autocabApiConfig);

			if(!isset($bookingId)) {
				throw new Exception("Required parameters booking_id is not provided", 400);
			}
			$booking = Booking::getById($db->getConnect(), $bookingId);
			if (count($booking) == 0) {
				throw new Exception( "Booking info not found for id = ${bookingId}", 400);
			}
			$details = json_decode($booking["details"], true);
			$vendorId = $details["vendor_id"];
			$authorizationRef = $details["authorization_ref"];

			if (!isset($authorizationRef)) {
				throw new Exception("Authorization Ref is not defined", 400);
			}

			$resp = $autocab->bookingCancellation($authorizationRef, $vendorId);
			$error = $resp["error"];
			$data = $resp["data"];
			if ($error != 0) {
				$message = $data["error"].": ".$data["error_description"].(isset($data["error_code"]) ? " Code:".$data["error_code"]:"");
				Booking::update($db->getConnect(), [
					"id"=>$bookingId,
					"message" => $message,
					"updated" => "now()",
					"completed" => "now()",
					"status" => Booking::STATUS_ERROR,
				]);
				throw new Exception($message);
			}
      list($booking) = Booking::update( $db->getConnect(), [
				"id"        => $booking["id"],
				"completed" => "now()",
				"updated"   => "now()",
				"status"    => Booking::STATUS_CANCELED,
				"message" => "Cancelled"
			] );
      $rawData = $resp["raw_data"];
      $request = $resp["request"];
      AutocabAPI::storeDetails($db, $booking["id"], "cancel", $rawData, $booking["message"], $request);
		} catch (Exception $e) {
			$message = $e->getMessage();
			$code = $e->getCode() != 0 ? $e->getCode() : 500;
			return $this->response( [	"error" => $message ], $code);
		}
		return $this->response([], 200);
	}

	public static function booking($autocab, $db, $bookingId, $memberId) {
		$log = [
			'message' => 'AutocabApi::booking',
			'bookingId' => $bookingId,
			'member_id' =>$memberId
		];
		Logger::debug($log);
		$message = "Failed to book trip";
		$code = 500;
		$action = "";
		$data = [];
		try {

		  $booking = Booking::getLastNotCompletedBookingForMember($db->getConnect(), $memberId);
      if (count($booking) > 0) {
        throw new Exception("The previous booking has not completed or not cancelled yet. Cancel the previous booking to make a new one.", 409);
      }

			if (!isset($bookingId)) {
				throw new Exception('Invalid Booking id');
			}
			$booking = Booking::getById($db->getConnect(), $bookingId);
			if (count($booking) == 0) {
				throw new Exception( "Booking info not found for id = ${bookingId}", 400);
			}
			$details = json_decode($booking["details"], true);
			$vendorId = $details["vendor_id"];
			$availabilityRef = $details["availability_ref"];

			$memberInfo = [];

			$q = "SELECT * FROM members WHERE id = $memberId LIMIT 1";
			$r = pg_query($db->getConnect(), $q);
			//syslog(LOG_WARNING,$q);
			if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
				$memberInfo["name"] = $f["firstname"]." ".$f["lastname"];
				$memberInfo["email"] = $f["email"];
				$memberInfo["email"] = $f["email"];
				$memberInfo["phone"] = $f["phone"];
			}
			$resp = $autocab->booking($availabilityRef,[$memberInfo], $vendorId);
			$error = $resp["error"];
			$data = $resp["data"];

			if ($error != 0) {
				$autocab->cancelAvailability($availabilityRef, $vendorId);
				$message = $data["error"].": ".$data["error_description"].(isset($data["error_code"]) ? "Code:".$data["error_code"]:"");
				Booking::update($db->getConnect(), [
					"id"=>$bookingId,
          "member_id" => $memberId,
					"message" => $message,
					"completed" => "now()",
					"updated" => "now()",
					"status" => Booking::STATUS_ERROR
				]);
				throw new Exception("Booking failed. " . $message);
			}

			$details["authorization_ref"] = $data["authorization_ref"];
			$details["booking_ref"] = $data["booking_ref"];
			list($booking) = Booking::update($db->getConnect(), [
				"id"=>$bookingId,
				"provider_booking_ref" => $data["authorization_ref"],
        "member_id" => $memberId,
				"message" => "Booked",
				"updated" => "now()",
				"status" => Booking::STATUS_BOOKED,
				"details" => json_encode($details)
			]);

			$rawData = $resp["raw_data"];
      $request = $resp["request"];
      AutocabAPI::storeDetails($db, $booking["id"], "booking", $rawData, $booking["message"], $request);
      if (isset($booking)) {
        $booking["details"] = $details;
      }
			return ["Trip booked",200,"response",$booking];
		} catch (Exception $e) {
			Logger::warning(['message' => 'AutocabApi::createReal exception', 'exception' => json_encode($e)]);
			syslog(LOG_WARNING,json_encode($e));
			$message = $e->getMessage();
		}
		Logger::warning(['message' => $message]);
		return [$message,$code,$action,$data];
	}

	public static function checkOffer($autocab, $db, $quoteId, $memberId, $bookingId ) {
		$log = [
			'message' => 'AutocabApi::checkOffer',
			'quoteId' => $quoteId,
		];

		Logger::debug($log);
		$message = "Failed to check offer";
		$code = 500;
		$action = "";
		$data = [];

		try {

		  $booking = Booking::getLastNotCompletedBookingForMember($db->getConnect(), $memberId);
		  if (count($booking) > 0) {
        throw new Exception("The previous booking has not completed or not cancelled yet. Cancel the previous booking to make a new one.", 409);
      }

			$quote = Quotes::getById($db->getConnect(), (int)$quoteId);
			if(!is_array($quote) || count($quote) == 0 || !isset($quote["automation_id"])) {
				throw new Exception("No quote found for id =${quoteId}", 400);
			}

			list($quoteService, $err) = Quotes::checkQuote($quote["automation_id"]);

			if ($err) {
				throw new Exception($err, 500);
			}

			if ($quoteService == null ||
			    $quoteService["complete"] =="" ||
			    $quoteService["message"] !="android_automation_job_detail") {
				throw new Exception("The quote has not completed yet for id =${quoteId}", 400);
			}

			list($details,$err) = Quotes::getQuoteDetails($quoteService["id"]);
			if ($details==NULL) {
				throw new Exception("The quote has not completed yet for id =${quoteId}", 400);
			}

			$low_estimate = $quoteService['cost'];
			$quoteServiceDetail = null;
			foreach ($details as $item) {
				$c_low_estimate = sprintf( "%0.02f", 0.01 * $item["low_estimate"] );
				if ($low_estimate==$c_low_estimate || $low_estimate>$c_low_estimate) {
					$quoteServiceDetail = $item;
					break;
				}
			}

			$vendorId = null;

			if (is_array($quoteServiceDetail)) {
				$realQuoteDetail = json_decode( $quoteServiceDetail["warning_message"], true );
				if ( is_array( $realQuoteDetail ) && isset( $realQuoteDetail["vendor_id"] ) ) {
					$vendorId = $realQuoteDetail["vendor_id"];
				}
			}

			$source = [
				'address' => $quoteService["location_start"],
				'latitude' => $quoteService["location_start_lat"],
				'longitude' => $quoteService["location_start_lng"]
			];

			$destination = [
				'address' => $quoteService["location_end"],
				'latitude' => $quoteService["location_end_lat"],
				'longitude' => $quoteService["location_end_lng"]
			];
			$resp = $autocab->getBookingAvailability($source, $destination, $vendorId);
			$data = $resp["data"];
			if ($resp["error"] != 0) {
				$message = $data["error"].": ".$data["error_description"].(isset($data["error_code"]) ? "Code:".$data["error_code"]:"");
				throw new Exception($message);
			}

      $vendorId = $data["vendor_id"];
			$quoteService["availability_ref"] = $availabilityRef = $data["availability_ref"];
			$quoteService["cost"] = sprintf( "%0.02f", 0.01 * $data["low_estimate"]);

			$requestData = [
				"quote_id" => $quoteId,
				"member_id" => $memberId,
				"provider_booking_ref" => $data["availability_ref"],
				"details" => json_encode(["vendor_id"=>$vendorId,"availability_ref" => $availabilityRef]),
				"status" => Booking::STATUS_INIT,
        "cost" => $data["low_estimate"]
			];

			if (isset($bookingId)) {
        $booking = Booking::getById($db->getConnect(), $bookingId);
        if (isset($booking) && $booking['member_id'] == $memberId && $booking['status'] == Booking::STATUS_INIT) {
          $requestData['id'] = $bookingId;
          list($booking,$err) = Booking::update($db->getConnect(), $requestData);
        } else {
          list($booking,$err) = Booking::create($db->getConnect(), $requestData);
        }
      } else {
        list($booking,$err) = Booking::create($db->getConnect(), $requestData);
      }
			$log = [
				'message' => 'viewReal Booking::create response',
				'booking' => $booking
			];
			Logger::debug($log);
			if($booking == null || !is_array($booking)) {
				$autocab->cancelAvailability($availabilityRef, $vendorId);
				$message = "Failed to create booking record: ".$err;
				throw new Exception($message);
			}

      $rawData = $resp["raw_data"];
      $request = $resp["request"];
			AutocabAPI::storeDetails($db, $booking["id"], "checkOffer", $rawData, $booking["message"],$request);

      $date = [
        "booking_id" => $booking["id"],
        "created" => $booking["created"],
        "availability_ref" => $availabilityRef,
        "vendor_id" => $vendorId,
        "quote_id" => $quoteService["id"],
        "location_start" => $quoteService["location_start"],
        "location_start_lat" => $quoteService["location_start_lat"],
        "location_start_lng" => $quoteService["location_start_lng"],
        "location_end" => $quoteService["location_end"],
        "location_end_lat" => $quoteService["location_end_lat"],
        "location_end_lng" => $quoteService["location_end_lng"],
        "cost_raw" => $quoteService["cost_raw"],
        "cost" => $quoteService["cost"]
      ];

			return ["Offer checked",200,"response", $date];
		} catch (Exception $e) {
			Logger::warning(['message' => 'AutocabApi::checkOffer exception', 'exception' => json_encode($e)]);
			syslog(LOG_WARNING,json_encode($e));
			$message = $e->getMessage();
      if ($e->getCode() > 0) {
        $code = $e->getCode();
      }
		}
		return [$message,$code,$action,$data];
	}

  public static function eventHandler($autocab, $db, $requestParams) {
    $log = [
      'message' => 'AutocabApi::eventHandler',
      'requestParams' => $requestParams,
    ];
    Logger::debug($log);

    $message = "Failed to handle event";
    $code = 200;
    $action = "";
    $data = [];

    try {
      if (!isset($requestParams) || empty($requestParams['methodName']) || empty($requestParams['payload'])) {
        throw new Exception("Bad request parameters ", 400);
      }

      $eventName = $requestParams['methodName'];
/*
      if (!in_array(strtolower($eventName),  array_map('strtolower',['AgentVehicleArrivedEventRequest','AgentBookingCancelledEventRequest', 'AgentPassengerOnBoardEventRequest']))) {
        throw new Exception("Unsupported event", 404);
      }
*/

      $payload = $requestParams['payload'];

      $inXml = new SimpleXMLElement( AutocabApi::decompress($payload));

      $bookingRef = (string)$inXml->BookingReference;
      $authRef = (string)$inXml->AuthorizationReference;
      $vendorId = (string)$inXml->Vendor->attributes()->Id;
      $vendorRef = (string)$inXml->Vendor->Reference;

      $xmlElement = NULL;
      $shortMessage = NULL;
      $dbCode = NULL;
      $dbData = NULL;
      $notifyUser = false;
      $storeDetails = true;

      switch ($eventName) {
        case 'AgentVehicleArrivedEventRequest':
          $xmlElement = $autocab->getTemplate('autocab_agent_vehicle_arrived_response.xml');
          list($shortMessage, $message, $dbCode, $dbData) = AutocabApi::vehicleArrivedEventHandler($inXml);
          $notifyUser = true;
          break;
        case 'AgentBookingCancelledEventRequest':
        case 'AgentNoFareEventRequest':
//          $xmlElement = $autocab->getTemplate('autocab_agent_booking_cancelled_response.xml');
          $eventShortName = substr($eventName, 0, strrpos($eventName, 'Request'));
          $xmlElement = $autocab->getTemplate('autocab_response.xml', 'AgentEvent', $eventShortName);
          list($shortMessage, $message, $dbCode, $dbData) = AutocabApi::bookingCancelledEventHandler($inXml);
          $notifyUser = ($dbCode == Booking::STATUS_VENDOR_CANCELED);
          $storeDetails = ($dbCode == Booking::STATUS_VENDOR_CANCELED);
          break;
        case 'AgentPassengerOnBoardEventRequest':
          $eventShortName = substr($eventName, 0, strrpos($eventName, 'Request'));
          $xmlElement = $autocab->getTemplate('autocab_response.xml', 'AgentEvent', $eventShortName);
          list($shortMessage, $message, $dbCode, $dbData) = AutocabApi::passengerOnBoardEventHandler($inXml);
          break;
        case 'AgentLocationUpdateEventRequest':
          $eventShortName = substr($eventName, 0, strrpos($eventName, 'Request'));
          $xmlElement = $autocab->getTemplate('autocab_response.xml', 'AgentEvent', $eventShortName);
          list($shortMessage, $message, $dbCode, $dbData) = AutocabApi::locationUpdateEventHandler($inXml);
          $storeDetails = false;
          break;
        case 'AgentBookingDispatchedEventRequest':
          $eventShortName = substr($eventName, 0, strrpos($eventName, 'Request'));
          $xmlElement = $autocab->getTemplate('autocab_response.xml', 'AgentEvent', $eventShortName);
          list($shortMessage, $message, $dbCode, $dbData) = AutocabApi::bookingDispatchedEventHandler($inXml);
          break;
        case 'AgentBookingCompletedEventRequest':
          $eventShortName = substr($eventName, 0, strrpos($eventName, 'Request'));
          $xmlElement = $autocab->getTemplate('autocab_response.xml', 'AgentEvent', $eventShortName);
          list($shortMessage, $message, $dbCode, $dbData) = AutocabApi::bookingCompletedEventHandler($inXml);
          break;
        default:
          $code = 404;
      }
      if ($code == 404) {
        throw new Exception("Unsupported event", 404);
      }

      if (!$xmlElement) {
        throw new Exception("Response template not found", 500);
      }
      $acknowledgementRef = Uuid::uuid4()->toString();
      $xmlElement->Vendor->attributes()->Id = $vendorId;
      $xmlElement->Vendor->Time = gmdate("Y-m-d\TH:i:s.v\Z");
      $xmlElement->Vendor->Reference = $vendorRef;
      $xmlElement->AcknowledgementReference = Uuid::uuid4()->toString();
      unset($xmlElement->Agent->Password);

      $booking = Booking::getByBookingRef($db->getConnect(), $authRef);

      if (count($booking) == 0) {
        $xmlElement->Result->Success = "false";
        $xmlElement->Result->FailureCode = 3;
        $xmlElement->Result->FailureReason = 'Booking not found.';
      } else {
        $bookingId = $booking["id"];
        $memberId = $booking["member_id"];
        $details = json_decode($booking["details"], true);
        $details['acknowledgement_ref'] = $acknowledgementRef;
        if (isset($dbData)) {
          $details['details'] = $dbData;
        }

        if (isset($dbCode)) {
          $completed = ($dbCode == Booking::STATUS_COMPLETED
                       || $dbCode == Booking::STATUS_CANCELED
                       || $dbCode == Booking::STATUS_VENDOR_CANCELED
                       || $dbCode == Booking::STATUS_ERROR) ? "now()" : null;
          $data = [
            "id"=>$bookingId,
            "message" => $message,
            "updated" => "now()",
            "completed" => $completed,
            "status" => $dbCode,
            "details" => json_encode($details)
          ];
          if (!isset($completed)) {
            unset($data["completed"]);
          }
          if (!isset($message)) {
            unset($data["message"]);
          }
          if (Booking::STATUS_LOCATION_UPDATE == $dbCode) {
            unset($data["status"]);
          }
          list($booking) = Booking::update($db->getConnect(), $data);
        }

        if ($storeDetails) {
          $rawData = $inXml;
          AutocabAPI::storeDetails($db, $bookingId, "event:${eventName}", $rawData, $booking["message"]);
        }

        if ($notifyUser) {
          $result =  AutocabApi::postOneSignal($db->getConnect(), $memberId, $shortMessage, $dbData);
          $log = [
            'message' => 'AutocabApi::eventHandler',
            'postOneSignalResult' => $result,
          ];
        }
        Logger::debug($log);
      }

      $template = str_replace('<?xml version="1.0"?>', '', $xmlElement->asXML());
      $data = [
        'payload'=> AutocabApi::compress($template)
      ];
      return ["Event $eventName handled", 200,"response", $data];
    } catch (Exception $e) {
      Logger::warning(['message' => 'AutocabApi::eventHandler exception', 'exception' => json_encode($e)]);
      syslog(LOG_WARNING,json_encode($e));
      $message = $e->getMessage();
      if ($e->getCode() > 0) {
        $code = $e->getCode();
      } else {
        $code = 500;
      }
    }
    return [$message,$code,$action,$data];

  }

  protected static function vehicleArrivedEventHandler($inXml) {
    $data = [
      'vehicle' => [
        'callsign' => (string)$inXml->Vehicle->Callsign,
        'make' => (string)$inXml->Vehicle->Make,
        'model' => (string)$inXml->Vehicle->Model,
        'colour' => (string)$inXml->Vehicle->Colour,
        'registration' => (string)$inXml->Vehicle->Registration,
        'plateNumber' => (string)$inXml->Vehicle->PlateNumber,
      ],
      'location' => [
        'time'=> (string)$inXml->Location->Time,
        'localTime'=> (string)$inXml->Location->LocalTime,
        'lat' => (float)$inXml->Location->Coordinate->Latitude,
        'lng' => (float)$inXml->Location->Coordinate->Longitude,
      ]
    ];
    $shortMessage = 'Vehicle Arrived';
    $message = 'Vehicle arrived';
    $code = Booking::STATUS_VEHICLE_ARRIVED;

    return [$shortMessage, $message, $code, $data];
  }

  protected static function bookingCancelledEventHandler($inXml) {
    $data = [
      'cancellationReason' => (string)$inXml->CancellationReason,
      'cancellationMsg' => (string)$inXml->CancellationMsg
    ];
    if ((string)$inXml->CancellationReason == 'CancelledByAgent') {
      $message = 'Booking cancelled. Cancellation Reason: '.(string)$inXml->CancellationReason.' Cancellation Message: '.(string)$inXml->CancellationMsg;
      $shortMessage = 'Booking cancelled';
      $code = Booking::STATUS_CANCELED;
    } else {
      $message = 'Booking cancelled by vendor. Cancellation Reason: '.(string)$inXml->CancellationReason.' Cancellation Message: '.(string)$inXml->CancellationMsg;
      $shortMessage = 'Booking cancelled by vendor';
      $code = Booking::STATUS_VENDOR_CANCELED;
    }
    return [$shortMessage, $message, $code, $data];
  }

  protected static function passengerOnBoardEventHandler($inXml) {
    $data = [
      'location' => [
        'time'=> (string)$inXml->Location->Time,
        'localTime'=> (string)$inXml->Location->LocalTime,
        'lat' => (float)$inXml->Location->Coordinate->Latitude,
        'lng' => (float)$inXml->Location->Coordinate->Longitude,
      ]
    ];
    $shortMessage = 'Passenger on board.';
    $message = 'Passenger on board.';
    $code = Booking::STATUS_IN_PROGRESS;
    return [$shortMessage, $message, $code, $data];
  }

  protected static function locationUpdateEventHandler($inXml) {
    $data = [
      'vehicle' => [
        'callsign' => (string)$inXml->Vehicle->Callsign,
        'make' => (string)$inXml->Vehicle->Make,
        'model' => (string)$inXml->Vehicle->Model,
        'colour' => (string)$inXml->Vehicle->Colour,
        'registration' => (string)$inXml->Vehicle->Registration,
        'plateNumber' => (string)$inXml->Vehicle->PlateNumber,
      ],
      'location' => [
        'time'=> (string)$inXml->Location->Time,
        'localTime'=> (string)$inXml->Location->LocalTime,
        'lat' => (float)$inXml->Location->Coordinate->Latitude,
        'lng' => (float)$inXml->Location->Coordinate->Longitude,
      ],
      'eta' => (string)$inXml->Eta,
      'localEta' => (string)$inXml->LocalEta,
      'etaInSeconds' => (string)$inXml->EtaInSeconds,
      'distance' => (string)$inXml->Distance,
    ];
    $shortMessage = 'Location updated';
    $message = null;
    $code = Booking::STATUS_LOCATION_UPDATE;
    return [$shortMessage, $message, $code, $data];
  }

  protected static function bookingDispatchedEventHandler($inXml) {
    $data = [
      'vehicle' => [
        'callsign' => (string)$inXml->Vehicle->Callsign,
        'make' => (string)$inXml->Vehicle->Make,
        'model' => (string)$inXml->Vehicle->Model,
        'colour' => (string)$inXml->Vehicle->Colour,
        'registration' => (string)$inXml->Vehicle->Registration,
        'plateNumber' => (string)$inXml->Vehicle->PlateNumber,
      ],
      'driver' => [
        'callsign' => (string)$inXml->Driver->Callsign,
        'firstName' => (string)$inXml->Driver->Make,
        'lastName' => (string)$inXml->Driver->Model,
        'phoneNumber' => (string)$inXml->Driver->MobileNumber,
        'badgeId' => (string)$inXml->Driver->BadgeId
      ],
      'location' => [
        'time'=> (string)$inXml->Location->Time,
        'localTime'=> (string)$inXml->Location->LocalTime,
        'lat' => (float)$inXml->Location->Coordinate->Latitude,
        'lng' => (float)$inXml->Location->Coordinate->Longitude,
      ],
      'eta' => (string)$inXml->Eta,
      'localEta' => (string)$inXml->LocalEta,
      'etaInSeconds' => (string)$inXml->EtaInSeconds
    ];
    $shortMessage = 'Booking dispatched.';
    $message = 'Booking dispatched.';
    $code = Booking::STATUS_DISPATCHED;
    return [$shortMessage, $message, $code, $data];
  }

  protected static function bookingCompletedEventHandler($inXml) {
    $data = [
      'location' => [
        'time'=> (string)$inXml->Location->Time,
        'localTime'=> (string)$inXml->Location->LocalTime,
        'lat' => (float)$inXml->Location->Coordinate->Latitude,
        'lng' => (float)$inXml->Location->Coordinate->Longitude,
      ]
    ];
    $shortMessage = 'Booking completed.';
    $message = 'Booking completed.';
    $code = Booking::STATUS_COMPLETED;
    return [$shortMessage, $message, $code, $data];
  }

  protected static function storeDetails($db, $bookingId, $action, $rawData, $message=NULL, $request=NULL) {
    list($bookingDetails) = Booking::createDetails($db->getConnect(), [
      "booking_id"=>$bookingId,
      "action" => $action,
      "details" => json_encode($rawData),
      "message" => $message,
      "request" => isset($request) ? json_encode($request) : $request
    ]);
    return $bookingDetails;
  }

  public static function bookingStatus($autocab, $db, $bookingId) {
    $log = [
      'message' => 'AutocabApi::bookingStatus',
      'bookingId' => $bookingId,
    ];
    Logger::debug($log);

    $message = "Failed to check booking status";
    $code = 500;
    $action = "";
    $data = [];

    try {
      if ( ! isset($bookingId ) || !is_numeric($bookingId) ) {
        throw new Exception( "Bad request. The booking id is not specified or not number. Booking id: ${bookingId}", 400 );
      }

      $booking = Booking::getById($db->getConnect(), $bookingId);
      if (count($booking) == 0) {
        throw new Exception( "The booking info not found for id = ${$bookingId}", 404);
      }

      $details = json_decode($booking["details"], true);
      $vendorId = $details["vendor_id"];
      $authorizationRef = $details["authorization_ref"];

      if (!isset($authorizationRef)) {
        throw new Exception("The Authorization Ref is not defined", 400);
      }

      $resp = $autocab->bookingStatus($authorizationRef, $vendorId);

      $error = $resp["error"];
      $data = $resp["data"];
      if ($error != 0) {
        $message = $data["error"].": ".$data["error_description"].(isset($data["error_code"]) ? " Code:".$data["error_code"]:"");
        throw new Exception($message);
      }
      return ["Booking checked",200,"response", $data];
    } catch (Exception $e) {
      Logger::warning(['message' => 'AutocabApi::bookingStatus exception', 'exception' => json_encode($e)]);
      syslog(LOG_WARNING,json_encode($e));
      $message = $e->getMessage();
      if ($e->getCode() > 0) {
        $code = $e->getCode();
      }
    }
    return [$message,$code,$action,$data];
  }

  public static function bookingInfo($db, $bookingId) {
    $log = [
      'message' => 'AutocabApi::bookingInfo',
      'bookingId' => $bookingId,
    ];
    Logger::debug($log);

    $message = "Failed to check booking status";
    $code = 500;
    $action = "";
    $data = [];

    try {
      if ( ! isset($bookingId ) || !is_numeric($bookingId) ) {
        throw new Exception( "Bad request. The booking id is not specified or not number. Booking id: ${bookingId}", 400 );
      }

      $booking = Booking::getById($db->getConnect(), $bookingId);
      if (count($booking) == 0) {
        throw new Exception( "The booking info not found for id = ${bookingId}", 404);
      }

      $details = json_decode($booking["details"], true);
      $booking["details"] = $details;

      $bookingDetailsList = Booking::getDetailsByBookingId($db->getConnect(), $bookingId);
      if (count($bookingDetailsList) > 0) {
        for ($i = 0; $i < count($bookingDetailsList); $i++) {
          $d =  json_decode($bookingDetailsList[$i]["details"], true);
          $bookingDetailsList[$i]["details"] = $d;
        }
      }
      $booking["bookingDetails"] = $bookingDetailsList;

      return ["Booking checked",200,"response", $booking];
    } catch (Exception $e) {
      Logger::warning(['message' => 'AutocabApi::bookingInfo exception', 'exception' => json_encode($e)]);
      syslog(LOG_WARNING,json_encode($e));
      $message = $e->getMessage();
      if ($e->getCode() > 0) {
        $code = $e->getCode();
      }
    }
    return [$message,$code,$action,$data];
	}



  public static function memberBookingList($db, $memberId, $params = []) {
    $log = [
      'message' => 'AutocabApi::memberBookingList',
      'bookingId' => $memberId,
      'params' => $params,
    ];
    Logger::debug($log);

    $message = "Failed to get member's booking list";
    $code = 500;
    $action = "";
    $data = [];

    try {
      if ( ! isset($memberId ) || !is_numeric($memberId) ) {
        throw new Exception( "Bad request. The member id is not specified or not number. Member id: ${$memberId}", 400 );
      }
      $data = Booking::getByMemberId($db->getConnect(), $memberId, $params);
      return ["Member's booking list ",200,"response", $data];
    } catch (Exception $e) {
      Logger::warning(['message' => 'AutocabApi::memberBookingList exception', 'exception' => json_encode($e)]);
      syslog(LOG_WARNING,json_encode($e));
      $message = $e->getMessage();
      if ($e->getCode() > 0) {
        $code = $e->getCode();
      }
    }
    return [$message,$code,$action,$data];
  }

  public static function bookingList($db, $params = []) {
    $log = [
      'message' => 'AutocabApi::bookingList',
      'params' => $params,
    ];
    Logger::debug($log);

    $message = "Failed to get booking list";
    $code = 500;
    $action = "";
    $data = [];

    try {
      $data = Booking::getAll($db->getConnect(), $params);
      return ["Booking list ",200,"response", $data];
    } catch (Exception $e) {
      Logger::warning(['message' => 'AutocabApi::bookingList exception', 'exception' => json_encode($e)]);
      syslog(LOG_WARNING,json_encode($e));
      $message = $e->getMessage();
      if ($e->getCode() > 0) {
        $code = $e->getCode();
      }
    }
    return [$message,$code,$action,$data];
  }


  private static function decompress($str) {
    return gzinflate(base64_decode($str));
  }

  private static function compress($str) {
    return base64_encode(gzdeflate($str, 9));
  }

  private static function postOneSignal($db, $memberId, $message, $arrayData) {
    global $savvyext;

    $url = $savvyext->cfgReadChar('onesignal.url');
    $app_id = $savvyext->cfgReadChar('onesignal.app_id');

    $devices = []; // reset the data

    $q = "SELECT * FROM members_devices WHERE status =1  AND member_id=${memberId} ";
    $r1 = pg_query($db, $q);
    while ($f1 = pg_fetch_assoc($r1)) {
      if (!empty($f1["player_id"])) {
        $devices[] = $f1["player_id"];
      }
    }
    $result = NULL;
    if (count($devices)) {
      $data = [
        'app_id' => $app_id,
        'contents' => ['en' => $message],
        'headings' => ['en' => 'Float Activities'],
        'subtitle' => ['en' => 'Your account activities message from Float.'],
        'content_available' => true,
        'mutable_content' => true,
        'include_player_ids' => $devices,
        'data' => $arrayData
      ];
      $opts = array(
        'http' => array(
          'method' => "POST",
          'header' =>
            "Content-Type: application/json; charset=utf-8\r\n" .
            "Accept: application/json\r\n",
          'content' => json_encode($data)
        )
      );

      $context = stream_context_create($opts);
      $body = file_get_contents($url, false, $context);
      $result = json_decode($body, true);
    }
    return $result;
  }


}