<?php

class QuoteGroupApi extends Api
{
    public $apiName = 'quotegroup';
    const CACHE_TIMEOUT = 300; // 5 minutes

    public function indexAction() {
        $message = 'Unhandled exception';
        $result = [];
        $data = $this->requestParams["data"] ?? [];
        error_log(json_encode( $this->requestParams));
        try {
            if (!is_array($data) || count($data)<1) {
                throw new Exception('Data not found');
            }
            $db = new Db();
            foreach ($data as $item) {
                $id = $item['quote_group_id'];
                if ($id<1) {
                    $item['quote_group_error'] = 'Missing or invalid quote group ID';
                    $item['quote_group'] = NULL;
                    $data[$key] = $item;
                    continue;
                }
                list($message,$code,$action,$data) = self::viewReal($db, $id);
                if ($code==200) {
                    $item['quote_group_error'] = NULL;
                    $item['quote_group'] = $data;
                } else {
                    $item['quote_group_error'] = $message;
                    $item['quote_group'] = NULL;
                }
                $result[] = $item;
            }
            return $this->response($result, 200);
        } catch (Exception $e) {
            $message = $e->getMessage();
            error_log('EXCEPTION: '.$message);
        }
        return $this->response(
            array(
                'error' => $message
            ), 404);
    }

    /**
     * Method GET
     * Get single record (by id)
     * http://DOMAIN/quote/1
     * @return string
     */
    public function viewAction() {
        //id must be the first parameter after /trips/x
        $id = array_shift($this->requestUri);        
        $db = new Db();
        list($message,$code,$action,$data) = self::viewReal($db, $id);
        if ($code==200) {
            return $this->response($data, 200);
        }
        return $this->response(
            array(
                'error'=> $message
            ), $code);
    }

    public static function viewReal($db, $id) {
        error_log('QuoteGroupApi::viewReal()');
        $message = 'Data not found';
        $code = 404;
        $action = '';
        $data = [];
        try {
            if(!$id || (int)$id<1) {
                throw new Exception('Invalid quote group ID');
            }
            $quoteGroup = QuoteGroup::getById($db->getConnect(), (int)$id);
            if(!is_array($quoteGroup) || count($quoteGroup)<1) {
                throw new Exception('Quote group was not found');
            }
            // Check "completed"
            if ($quoteGroup["completed"]!="" && (int)$quoteGroup["cost"]!=0) {
                error_log('Quote group #'.$id.' was completed with the cost '.$quoteGroup["cost"]);
                list($quotes,$err) = Quotes::getByQuoteGroupId($db->getConnect(), (int)$id);
                if ((!is_array($quotes) || count($quotes)<1) && $quoteGroup["quote_id"]>0) {
                    $quotes = [];
                    array_push($quotes, Quotes::getById($db->getConnect(), $quoteGroup["quote_id"]));
                }
                $quoteGroup["quotes"] = $quotes; // Does not matter much if we have "cost"
                $quoteGroup["deeplink"] = Quotes::deeplink($db->getConnect(), $quoteGroup);
                return ["OK",200,"",$quoteGroup]; // OK!
            } else {
                error_log('Quote group #'.$id.' was completed yet...');
            }
            // Load quotes
            list($quotes,$err) = Quotes::getByQuoteGroupId($db->getConnect(), (int)$id);
            if (!is_array($quotes) || count($quotes)<1) {
                if ($quoteGroup["quote_id"]>0) {
                    $quotes = [];
                    array_push($quotes, Quotes::getById($db->getConnect(), $quoteGroup["quote_id"]));
                    error_log(json_encode($quotes));
                } else {
                    throw new Exception ($err!=''?$err:'Failed to load quotes');
                }
            } else {
                error_log(json_encode($quotes));
            }
            // Check each quote
            $completed = 0;
            $lowestQuote = NULL;
            $cost = PHP_INT_MAX;
            foreach ($quotes as $quote) {
                for ($i=0; $i<3; $i++) {
                    if ($quote["completed"]=="" && $quote["travel_date"]=="" && $quote["automation_id"]>0) {
                        if ($i>0) {
                            sleep(3); // wait...
                        }
                        // Call service if needed
                        list($quoteService, $err) = QuoteAPI::checkQuote($quote["automation_id"]);
                        // Update
                        if (is_array($quoteService) && $quoteService["complete"]!="") {
                            $quoteService["automation_id"] = $quoteService["id"];
                            $quoteService["id"] = (int)$quote["id"];
                            $quoteService["completed"] = $quoteService["complete"];
                            $timezone = Address::getTzByAddressId($db->getConnect(),$quote["location_start_id"]);
                            $quoteService["travel_date"] = Utilities::convertUtcToLocal($quoteService["complete"],$timezone);
                            $quoteService["created"] = $quote["created"];
                            $quoteService["location_start_id"] = $quote["location_start_id"];
                            $quoteService["location_end_id"] = $quote["location_end_id"];
                            $quoteService["member_id"] = $quote["member_id"];
                            $quoteService["deeplink"] = Quotes::deeplink($db->getConnect(), $quote);
                            list ($res, $err) = Quotes::update($db->getConnect(), $quoteService);
                            if ($quoteService["cost"]==0 && 
                                $quoteService["message"]=="android_automation_job_detail") {
                                // We must take the details
                                $quoteService = QuoteApi::quoteDetails($db, $quoteService);
                            }
                            $quote = $quoteService;
                        }
                    } else {
                        break;
                    }
                }
                // Check "completed"
                if ($quote["completed"]!="" && $quote["travel_date"]!="" && (int)$quote["cost"]!=0) {
                    error_log('Completed!');
                    $completed++;
                    if ($quote["cost"] < $cost) {
                        $cost = $quote["cost"];
                        $lowestQuote = $quote;
                    }
                }
            }
            $quoteGroup["cost"] = $cost==PHP_INT_MAX ? NULL : $cost;
            $quoteGroup["transport_provider_id"] = is_array($lowestQuote) ? $lowestQuote["transport_provider_id"] : NULL;
            $quoteGroup["quote_id"] = is_array($lowestQuote) ? $lowestQuote["id"] : NULL;
            $quoteGroup["processed"] = $completed;
            // All completed?
            if ($completed>=$quoteGroup["transport_providers"]) {
                // All quotes completed - hurray!
                $quoteGroup["completed"] = date("Y-m-d H:i:s");  
            } else if (is_array($lowestQuote)) {
                // We will update since we have at least one quote completed
                $quoteGroup["completed"] = NULL;
            } else {
                // Do not update, as nothing has completed
                $quoteGroup["quotes"] = $quotes;
                return ["OK",200,"",$quoteGroup];
            }
            list($res,$err) = QuoteGroup::update($db->getConnect(), $quoteGroup);
            if ($res==NULL || !array_key_exists("id",$res)) {
                error_log('QuoteGroup::update() failed?');
                error_log(json_encode($res));
                $res = $quoteGroup;
            }
            $res["quotes"] = $quotes;
            if (isset($res["transport_provider_id"]) && $res["transport_provider_id"]>0) {
                $res["deeplink"] = Quotes::deeplink($db->getConnect(), $res);
            }
            error_log(json_encode($res));
            return ["OK",200,"",$res];
        } catch (Exception $e) {
            $code = 500;
            $message = $e->getMessage();
            error_log('EXCEPTION: '.$message);
        }
        return [$message,$code,$action,$data];
    }

    public function createAction() {
        $origin = $this->requestParams["origin"] ?? "";
        $destination = $this->requestParams["destination"] ?? "";
        $member_id = $this->requestParams["member_id"] ?? 0;
        $country = $this->requestParams["country"] ?? GeocodeApi::DEFAULT_COUNTRY_CODE;
        $transport_providers = $this->requestParams["transport_providers"] ?? [];

        $db = new Db();

        // DEBUG
        $country = Geocode::mockGPSCountry($db->getConnect(), $member_id, $country, 'default');

        list($message,$code,$action,$data) = QuoteGroupApi::createReal(
            $db, $origin, $destination, $member_id, $transport_providers, $country
        );
        if ($code==200) {
            if ($action=="view") {
                $id = $data["id"];
                array_unshift($this->requestUri, $id);
                return $this->viewAction();
            } else if ($action=="response") {
                return $this->response($data, 200);
            }
        }
        return $this->response(
            array(
                "error" => $message
            ), $code);
    }

    public static function createReal($db, $origin, $destination, $member_id, $transport_providers, $country=GeocodeApi::DEFAULT_COUNTRY_CODE) {
        syslog(LOG_WARNING,"QuoteGroupApi::createReal(\$db, $origin, $destination, $member_id, \$transport_providers, $country)");
        $message = "Failed to schedule quotes";
        $code = 500;
        $action = "";
        $data = [];
        $city = '';
        try {
            foreach ($transport_providers as $key=>$transport_provider_id) {
                if (!in_array($transport_provider_id, [1,2,3,4,5,8])) {
                    unset($transport_providers[$key]);
                }
                if (in_array($transport_provider_id, [3,4,5])) {
                    $city = 'Singapore';
                }
                if (in_array($transport_provider_id, [1,2,8])) {
                    $city = 'Other';
                }
            }
            if (count($transport_providers)<1) {
                throw new Exception('Unsupported transport provider IDs');
            }
            if (!$destination || !$origin) {
                throw new Exception('Invalid origin and/or destination address');
            }
            // Check if exists
            list($total, $quoteGroups) = QuoteGroup::getByOriginDestinationId(
                $db->getConnect(), 
                $origin, $destination, 0 /* transport_provider_id */,
                1, 0); // LIMIT # OFFSET #
            if (is_array($quoteGroups) && count($quoteGroups)>0) {
                $quoteGroup = $quoteGroups[0];
                $id = $quoteGroup["id"]; // Will return the last quote
                $completed = $quoteGroup["completed"]; // Completed with the SELF::CACHE_TIMEOUT
                if (time()-strtotime($completed)<SELF::CACHE_TIMEOUT) {
                    return ["Quote from cache",200,"view",["id"=>$id]];
                }
            }
            // Geocode origin and destination
            $headers = getallheaders();
            $client_id = isset($headers['client_id'])?$headers['client_id']:"Unknown";
            list($originData, $err) = GeocodeAPI::geocode($db, $origin, $country,$client_id);
            $log = [
                'message' => 'QuoteGroupApi::geocode origin',
                'data' => ['client_id'=>$client_id, 'address_input'=>$origin],
                'errror' => $err
            ];
            Logger::debug($log);
            if ($err || !isset($originData["lat"])) throw new Exception("Origin geocoding failed: ".$err);
            list($destinationData, $err) = GeocodeAPI::geocode($db, $destination, $country, $client_id);
            $log = [
                'message' => 'QuoteGroupApi::geocode destination',
                'data' => ['client_id'=>$client_id, 'address_input'=>$destination],
                'errror' => $err
            ];
            Logger::debug($log);
            if ($err || !isset($destinationData["lat"])) throw new Exception("Destination geocoding failed: ".$err);

            $isOriginWithin = false;
            $isDestinationWithin = false;
            // Check if we are within Singapore: for Grab, Gojek, CDG

            if ($city=='Singapore') {
                $sgLat  = 1.352083;
                $sgLng  = 103.819836;
                $radius = 25; // km
                $isOriginWithin = GeocodeAPI::withinCity(
                    $originData["address"],$originData["lat"],$originData["lng"],
                    $city,$sgLat,$sgLng,$radius);
                $isDestinationWithin = GeocodeAPI::withinCity(
                    $destinationData["address"],$destinationData["lat"],$destinationData["lng"],
                    $city,$sgLat,$sgLng,$radius);
            }
            // Uber & Lyft: no check required
            if ($city=='Other') {
                $isOriginWithin = true;
                $isDestinationWithin = true;
            }
            
            if ($isOriginWithin && $isDestinationWithin) {
                syslog(LOG_WARNING,"'".$originData["address"]."' and '".$destinationData["address"]."' are within Singapore");
            } else {
                throw new Exception("Origin and/or destination is not within the service area!");
            }
            // Create quote group
            $requestData = [
                "location_start_lat" => $originData["lat"],
                "location_start_lng" => $originData["lng"],
                "location_start" => $originData["address"] ?? $origin,
                "location_start_country" => $originData["country"],
                "location_end_lat" => $destinationData["lat"],
                "location_end_lng" => $destinationData["lng"],
                "location_end" => $destinationData["address"] ?? $destination,
                "location_end_country" => $destinationData["country"],
                "transport_providers" => $transport_providers
            ];
            list($quoteGroup,$err) = QuoteGroup::create($db->getConnect(), $requestData);
            if ($quoteGroup==NULL || !isset($quoteGroup["id"])) {
                throw new Exception("Quote group creation failed: " . ($err ?? $message));
            } 
            $id = $quoteGroup["id"];
            syslog(LOG_WARNING,'New QuoteGroup ID: '.$id);
            // Schedule the quotes
            //$quoteApi = new QuoteApi($this->requestUri, false /* no encryption for internal call */);
            $quotes = [];
            foreach ($transport_providers as $transport_provider_id) {
                //$quoteApi->requestParams["transport_provider_id"] = $transport_provider_id;
                list($message,$code,$action,$data) = QuoteApi::createReal(
                    $db, $origin, $destination, $member_id, $transport_provider_id, $id, 0, $country, 'f'
                );
                $quote_id = 0;
                if ($code==200 && ($action=="view" || $action=="response")) {
                    $quote_id = $data["id"];
                    $quotes[] = $quote_id;
                } else {
                    syslog(LOG_WARNING,'ERROR: '.$message);
                }
                syslog(LOG_WARNING,'QUOTE: '.json_encode($data));
            }
            $quoteGroup["cost"] = NULL;
            $quoteGroup["transport_provider_id"] =  count($quotes)==1 ? $transport_provider_id : NULL;
            $quoteGroup["quote_id"] = count($quotes)==1 ? $quotes[0] : NULL;
            $quoteGroup["completed"] = NULL;
            if (count($quotes)<1) {
                $quoteGroup["completed"] = date("Y-m-d H:i:s");
                $quoteGroup["processed"] = 0;
            }
            list($res,$err) = QuoteGroup::update($db->getConnect(), $quoteGroup);
            if ($res==NULL || !isset($res["id"])) {
                $res = $quoteGroup;
            }
            $res["quotes"] = $quotes;
            return ["Group quote scheduled",200,"response",$res];
        } catch (Exception $e) {
            syslog(LOG_WARNING,json_encode($e));
            $message = $e->getMessage();
            syslog(LOG_WARNING,'EXCEPTION: '.$message);
        }
        return [$message,$code,$action,$data];
    }

    public function updateAction() {
        return $this->response(
            array(
                "error" => "Update error"
            ), 400);
    }

    public function deleteAction() {
        return $this->response(
            array(
                "error" => "Delete error"
            ), 500);
    }

}
