<?php
/**
 * Data manipulation for the model
 */
 class ShopModel{
     const PACKAGE_TYPES = [
         'ID_STANDARD_TYPE' => 1,
         'ID_OPTION_TYPE' => 2,
         'ID_ADDITONAL_TYPE' => 3
     ];

     const DOCUMENT_TYPES = [
         'ID_TEMPLATE_QUESIONNAIRE' => 1,
         'ID_CUSTOMER_QUESTIONNAIRE' => 2,
         'ID_TEMPLATE_AGREEMENT' => 6
     ];

     const PRODUCT_CATEGORIES = [
         'ITEMS' => 1,
         'INSTALLATION' => 2
     ];

     private function getPricesForPackages($idCommercialLead, $idPackage, $type){
         global $database, $user;
         $data = [];
         $whereSql = "";
         $extraJoin = "";

         if($type === self::PACKAGE_TYPES['ID_STANDARD_TYPE']){
              $whereSql = "AND plcl.idPackage=$idPackage";
         }

         if($type === self::PACKAGE_TYPES['ID_OPTION_TYPE']){
              $whereSql = "AND pog.idPackage=$idPackage";
              $extraJoin = "INNER JOIN ".TABLES['rel_group_options']." rgo
                                ON rgo.idOptionPackage=plcl.idPackage
                             INNER JOIN ".TABLES['package_option_groups']." pog
                                ON pog.id=rgo.idGroup";
             }

         if($type === self::PACKAGE_TYPES['ID_ADDITONAL_TYPE']){
              $whereSql = "AND rap.idPackage=$idPackage";
              $extraJoin = " INNER JOIN ".TABLES['rel_additional_packages']." rap
                            ON rap.idAdditionalPackage=plcl.idPackage";
         }

         $sql = "SELECT
                    plcl.id as idPrice,
                    plb.idPackage,
                    plb.idPaymentType,
                    pt.payType,
                    pt.packagePayPeriod,
                    pt.servicesContractPeriod,
                    pt.periodUnit,
                    pt.maxContractPeriod,
                    (plcl.fixedExtra + plb.minimalFixedPrice) AS fixedExtra,
                    (plcl.recurentExtra + plb.minimalRecurentPrice) AS recurentExtra,
                    (plcl.servicesExtra + plb.minimalServicesPrice) AS servicesExtra,
                    plb.minimalServicesPrice as servicePrice
                FROM ".TABLES['price_list_broker']." plb
                INNER JOIN
                    (SELECT
                        plcl.idPackage,
                            plcl.idPaymentType,
                            plcl.idCommercialLead,
                            MAX(IFNULL(idCustomer, 0)) AS idCustomer
                    FROM
                        ".TABLES['price_list_commercial_lead']." plcl
                        $extraJoin
                        LEFT OUTER JOIN ".TABLES['customers']." cust
                            ON cust.id = plcl.idCustomer
                    WHERE
                        (cust.idUser = ".$user->getUserId()." OR cust.idUser IS NULL)
                         $whereSql
                         AND plcl.idCommercialLead=$idCommercialLead
                    GROUP BY plcl.idPackage , plcl.idPaymentType
                ) AS custom_prices
                    ON plb.idPackage = custom_prices.idPackage
                        AND plb.idPaymentType = custom_prices.idPaymentType
                    INNER JOIN ".TABLES['price_list_commercial_lead']." plcl
                        ON plcl.idPackage = custom_prices.idPackage
                            AND plcl.idPaymentType = custom_prices.idPaymentType
                            AND plcl.idCommercialLead = custom_prices.idCommercialLead
                            AND IFNULL(plcl.idCustomer, 0) = custom_prices.idCustomer
                            AND plcl.visibleToCustomer = 1
                    INNER JOIN ".TABLES['payment_types']." pt
                        ON pt.id = plb.idPaymentType";

        $query = $database->query($sql);
        while($row = $database->fetchArray($query)){
            $data[$row['idPackage']][] = $row;
        }

        return $data;
     }

     private function getCommercialLeadInfo($idCommercialLead){
         global $database;

         $sql = "SELECT
                    cl.name,
                    cl.mail,
                    cl.phone
                FROM ".TABLES['commercial_leads']." cl
                    WHERE cl.id=$idCommercialLead
                ";

         return $database->fetchResultArray($sql);
     }

     public function getShopPackages($idCommercialLead, $idPackage = 0, $search = ''){
         global $database, $user;

         $whereSql = "WHERE p.status='available' AND p.idPackageType=".self::PACKAGE_TYPES['ID_STANDARD_TYPE']." ";
         $search = $database->escapeValue($search);

         if($idPackage !== 0){
             $whereSql .= " AND p.id=$idPackage";
         }

         if($search !== ''){
             $searchValues = explode(' ', $search);
             $packageCondition = '';
             $descriptionCondition = '';
             $referenceCondition = '';
             $countryCondition = '';
             foreach ($searchValues as $valueToSearch) {
                 if($valueToSearch !== ''){
                     $packageCondition .= "p.name like '%".$valueToSearch."%'"." OR ";
                     $descriptionCondition .= " p.description like '%".$valueToSearch."%'"." OR ";
                     $referenceCondition .= "p.reference='".$valueToSearch."' OR ";
                     $countryCondition .= "c.name='".$valueToSearch."' OR ";
                 }
             }
             $countryCondition = rtrim($countryCondition, ' OR ');
             $whereSql .= " AND ( $packageCondition
                                 $descriptionCondition
                                 $referenceCondition
                                 $countryCondition)";
         }

         $sql = "SELECT
                    p.id AS idPackage,
                    p.reference,
                    p.name,
                    p.description AS shortDescription,
                    price_selection.idCommercialLead,
                    c.name AS country,
                    c.code AS countryCode
                FROM
                    ".TABLES['packages']." p
                        INNER JOIN
                    ".TABLES['countries']." c ON c.id = p.idCountry
                    INNER JOIN ".TABLES['rel_package_processes']." rpp
                        ON rpp.idPackage=p.id
                    INNER JOIN
                    (SELECT
                        idPackage,
                            plcl.idPaymentType,
                            plcl.idCommercialLead,
                            MAX(IFNULL(idCustomer, 0)) AS idCustomer
                    FROM
                        ".TABLES['price_list_commercial_lead']." plcl
                    LEFT OUTER JOIN ".TABLES['customers']." cust
                        ON cust.id = plcl.idCustomer
                    WHERE
                        (cust.idUser = ".$user->getUserId()." OR cust.idUser IS NULL)
                        AND plcl.idCommercialLead = $idCommercialLead
                    GROUP BY plcl.idPackage , plcl.idPaymentType
                    ) AS price_selection
                        ON price_selection.idPackage = p.id
                    INNER JOIN ".TABLES['price_list_commercial_lead']." plcl
                        ON plcl.idPackage = price_selection.idPackage
                        AND plcl.idPaymentType = price_selection.idPaymentType
                        AND plcl.idCommercialLead = price_selection.idCommercialLead
                        AND IFNULL(plcl.idCustomer, 0) = price_selection.idCustomer
                        AND plcl.visibleToCustomer = 1
                    $whereSql
                GROUP BY p.id
                ORDER BY p.id DESC";

         $data = [];
         $query = $database->query($sql);
         while($row = $database->fetchArray($query)){
             if(strlen($row['shortDescription']) > 300 && $idPackage === 0){
                 $row['shortDescription'] = substr($row['shortDescription'], 0, 300) . '...';
             }
             $data[] = $row;
         }

         return $data;
     }

     /**
      * get package options
      * @param  INT $idPackage        id for the pacakge
      * @param  INT $idCommercialLead id for the comemrcial lead
      * @return HashArray             list of option packages grouped by group id
      */
     private function getPackageOptions($idPackage, $idCommercialLead){
         global $database;
         $data = [];

         $packageOptionPrices = $this->getPricesForPackages($idCommercialLead, $idPackage, self::PACKAGE_TYPES['ID_OPTION_TYPE']);

         $sql = "SELECT
                    rgo.idOptionPackage,
                    p.name AS optionName,
                    rgo.isDefault,
                    pog.id AS idGroup,
                    pog.name AS groupName
                FROM ".TABLES['package_option_groups']." pog
                INNER JOIN ".TABLES['rel_group_options']." rgo
                    ON rgo.idGroup=pog.id
                INNER JOIN ".TABLES['packages']." p
                    ON p.id=rgo.idOptionPackage
                WHERE pog.idPackage=$idPackage";
         $query = $database->query($sql);
         while($row = $database->fetchArray($query)){
             $idGroup = $row['idGroup'];
             $data[$idGroup]['idGroup'] = $row['idGroup'];
             unset($row['idGroup']);
             $data[$idGroup]['groupName'] = $row['groupName'];
             unset($row['groupName']);
             $row['prices'] = isset($packageOptionPrices[$row['idOptionPackage']]) ? $packageOptionPrices[$row['idOptionPackage']] : [];
             $data[$idGroup]['options'][] = $row;
         }

         return $data;
     }

     /**
      * get package documents
      * @param  INT $idPackage        id for the pacakge
      * @return Array                 list of documents
      */
     private function getPackageDocuments($idPackage){
         global $database;

         $sql = "SELECT
                    d.id AS idDocument,
                    d.documentName,
                    d.extension
                FROM ".TABLES['documents']." d
                INNER JOIN ".TABLES['document_types']." dt
                    ON dt.id=d.idDocumentType
                INNER JOIN ".TABLES['rel_package_documents']." rpd
                    ON rpd.idDocument=d.id
                WHERE rpd.idPackage=$idPackage ";

         return $database->fetchResultArray($sql);
     }

     /**
      * get package additional pacakges
      * @param  INT $idPackage        id for the pacakge
      * @param  INT $idCommercialLead id for the comemrcial lead
      * @return Array             list of additonal packages
      */
     private function getAdditionalPackages($idPackage, $idCommercialLead){
         global $database;
          $data = [];

         $additionalPackagesPrices = $this->getPricesForPackages($idCommercialLead, $idPackage, self::PACKAGE_TYPES['ID_ADDITONAL_TYPE']);
         $sql = "SELECT
                    rap.idAdditionalPackage,
                    p.name AS packageName
                FROM ".TABLES['rel_additional_packages']." rap
                INNER JOIN ".TABLES['packages']." p
                    ON p.id=rap.idAdditionalPackage
                WHERE rap.idPackage=$idPackage";

         $query = $database->query($sql);
         while($row = $database->fetchArray($query)){
             $row['prices'] = isset($additionalPackagesPrices[$row['idAdditionalPackage']]) ? $additionalPackagesPrices[$row['idAdditionalPackage']] : [];
             $data[] = $row;
         }

         return $data;
     }

     /**
      * get info for a pacakge that can be sold in the co-market
      * @param  INT $idCommercialLead id for the commetcial lead
      * @param  INT $idPackage        id for the package
      * @return HashArray             pacakge details(country, prices, info, documents, options, additional package)
      */
     public function getShopPackageDetails($idCommercialLead, $idPackage){
         global $database, $user;
         $countries = new Countries();

         $idPackage = $database->escapeValue($idPackage);
         $data = [];
         $data['country'] = $countries->getCurrencyForPackage($idPackage);
         $data['prices'] = $this->getPricesForPackages($idCommercialLead, $idPackage, self::PACKAGE_TYPES['ID_STANDARD_TYPE']);
         $data['prices'] = isset($data['prices'][$idPackage]) ? $data['prices'][$idPackage] : [];

         if(empty( $data['prices'])){
             return [];
         }
         $data['documents'] = $this->getPackageDocuments($idPackage);
         $shopPackages = $this->getShopPackages($idCommercialLead, $idPackage);
         $data['packageInfo'] = isset($shopPackages[0]) ? $shopPackages[0] : [];
         $commerciaLeads = $this->getCommercialLeadInfo($idCommercialLead);
         $data['commercialLead'] = isset($commerciaLeads[0]) ? $commerciaLeads[0] : [];
         $data['groups'] = $this->getPackageOptions($idPackage, $idCommercialLead);
         $data['additionalPackages'] = $this->getAdditionalPackages($idPackage, $idCommercialLead);

         return $data;
     }

     /**
      * get all commercial leads linked to a customer
      * @return Array list of commercial leads
      */
     public function getAllCommercialLeads(){
         global $database, $user;

         $sql = "SELECT rclc.idCommercialLead,
                        cl.name as commercialLeadName
                 FROM ".TABLES['rel_commercial_lead_customers']." rclc
                 INNER JOIN ".TABLES['customers']." c
                     ON c.id=rclc.idCustomer
                 INNER JOIN ".TABLES['commercial_leads']." cl
                     ON cl.id=rclc.idCommercialLead
                 WHERE rclc.isLinkEnabled=1 AND c.idUser=" . $user->getUserId();

         return  $database->fetchResultArray($sql);
     }

     /**
      * get options for items in the cart
      * @return Array list of options grouped by cart id
      */
     private function getShopCartOption(){
         global $database, $user;
         $data = [];
         $sql = "SELECT
                    wscep.idCart,
                    p.name AS packageName,
                    pog.name AS groupName,
                    plb.minimalFixedPrice AS fixedPrice,
                    plb.minimalRecurentPrice AS recurrentPrice,
                    plb.minimalServicesPrice AS servicesPrice
                FROM ".TABLES['web_shop_cart_extra_packages']." wscep
                INNER JOIN  ".TABLES['packages']." p
                    ON p.id=wscep.idExtraPackage
                INNER JOIN ".TABLES['web_shop_cart']." wsc
                    ON wsc.id=wscep.idCart
                INNER JOIN ".TABLES['rel_group_options']." rgo
                    ON rgo.idOptionPackage=wscep.idExtraPackage
                INNER JOIN ".TABLES['package_option_groups']." pog
                    ON pog.id=rgo.idGroup
                INNER JOIN ".TABLES['price_list_commercial_lead']." plcl
                    ON plcl.id=wsc.idPrice
                LEFT OUTER JOIN ".TABLES['price_list_broker']." plb
                    ON plb.idPackage=wscep.idExtraPackage AND plb.idPaymentType=plcl.idPaymentType
                WHERE wsc.idUser=".$user->getUserId();
         $query = $database->query($sql);
         while($row = $database->fetchArray($query)){
             $row['isAvailable'] = !is_null($row['fixedPrice']);
             $data[$row['idCart']]['options'][] = $row;
             if(!isset($data[$row['idCart']]['areOptionsAvailable'])){
                 $data[$row['idCart']]['areOptionsAvailable'] = true;
             }
             $data[$row['idCart']]['areOptionsAvailable'] = ($data[$row['idCart']]['areOptionsAvailable'] && $row['isAvailable']);
         }

         return $data;
     }
     private function getShopCartAdditionalPackages(){
         global $database, $user;

         $data = [];
         $sql = "SELECT
                    wscep.idCart,
                    p.name AS packageName,
                    plb.minimalFixedPrice AS fixedPrice,
                    plb.minimalRecurentPrice AS recurrentPrice,
                    plb.minimalServicesPrice AS servicesPrice
                FROM ".TABLES['web_shop_cart_extra_packages']." wscep
                INNER JOIN  ".TABLES['packages']." p
                    ON p.id=wscep.idExtraPackage
                INNER JOIN ".TABLES['web_shop_cart']." wsc
                    ON wsc.id=wscep.idCart
                INNER JOIN ".TABLES['price_list_commercial_lead']." plcl
                    ON plcl.id=wsc.idPrice
                LEFT OUTER JOIN ".TABLES['price_list_broker']." plb
                    ON plb.idPackage=wscep.idExtraPackage AND plb.idPaymentType=plcl.idPaymentType
                WHERE wsc.idUser=".$user->getUserId()." AND p.idPackageType=".self::PACKAGE_TYPES['ID_ADDITONAL_TYPE'];
         $query = $database->query($sql);
         while($row = $database->fetchArray($query)){
             $row['isAvailable'] = !is_null($row['fixedPrice']);
             $data[$row['idCart']]['additionalPackages'][] = $row;
             if(!isset($data[$row['idCart']]['areAdditionalAvailable'])){
                 $data[$row['idCart']]['areAdditionalAvailable'] = true;
             }
             $data[$row['idCart']]['areAdditionalAvailable'] = ($data[$row['idCart']]['areAdditionalAvailable'] && $row['isAvailable']);
         }

         return $data;
     }

     /**
      * get cart items Details
      * @return array list of items in the cart
      */
     public function getShopCart(){
         global $database, $user;
         $data = [];
         $options = $this->getShopCartOption();
         $additionalPackages = $this->getShopCartAdditionalPackages();

         $sql = "SELECT
                    cart.id as idCart,
                    cart.idPackage,
                    cart.idCustomerInstance,
                    cart.idPrice,
                    cart.packageInstance,
                    p.status,
                    rclc.isLinkEnabled,
                    p.name as packageName,
                    cl.name as commercialLead,
                    pt.payType,
                    pt.periodUnit AS periodUnit,
                    pt.id AS idPayType,
                    (plcl.fixedExtra + plb.minimalFixedPrice) AS fixedPrice,
                    (plcl.recurentExtra + plb.minimalRecurentPrice) AS recurentPrice,
                    (plcl.servicesExtra + plb.minimalServicesPrice) AS servicesPrice,
                    cart.quantity
                FROM ".TABLES['web_shop_cart']." cart
                INNER JOIN ".TABLES['packages']." p
                    ON p.id=cart.idPackage
                INNER JOIN ".TABLES['rel_commercial_lead_customers']." rclc
                    ON rclc.id=cart.idCustomerInstance
                INNER JOIN ".TABLES['commercial_leads']." cl
                    ON cl.id=rclc.idCommercialLead
                INNER JOIN ".TABLES['price_list_commercial_lead']." plcl
                    ON plcl.id=cart.idPrice
                INNER JOIN ".TABLES['payment_types']." pt
                    ON pt.id=plcl.idPaymentType
                INNER JOIN ".TABLES['price_list_broker']." plb
                    ON plb.idPackage=plcl.idPackage AND plb.idPaymentType=plcl.idPaymentType
                WHERE cart.idUser=".$user->getUserId()."
                ";
        $query = $database->query($sql);
        while($row = $database->fetchArray($query)){
            $row['options'] = isset($options[$row['idCart']]) ? $options[$row['idCart']]['options'] : [];
            $row['areOptionsAvailable'] = isset($options[$row['idCart']]) ? $options[$row['idCart']]['areOptionsAvailable'] : true;
            $row['additionalPackages'] = isset($additionalPackages[$row['idCart']]) ? $additionalPackages[$row['idCart']]['additionalPackages'] : [];
            $row['areAdditionalAvailable'] = isset($additionalPackages[$row['idCart']]) ? $additionalPackages[$row['idCart']]['areAdditionalAvailable'] : true;
            $data[] = $row;
        }

        return $data;
     }

     /**
      * insert new option when adding item to cart
      * @param  INT $idCart     id for the cart
      * @param  Object $options options to be added
      * @return INT             number of inserted items
      */
     private function insertOptionsToCart($idCart, $options){
         global $database;

         if(empty((array) $options)){
             return 0;
         }

         $sql = "INSERT INTO ".TABLES['web_shop_cart_extra_packages']."
                (idCart, idExtraPackage)
                VALUES ";
         foreach ($options as $idOption => $idOptionValue) {
             $idOptionValue = $database->escapeValue($idOptionValue);
             $sql .= "($idCart, $idOptionValue),";
         }
         $sql = rtrim($sql, ",");
         $query = $database->query($sql);

         return $database->affectedRows();
     }

     /**
      * add new items to the cart
      * @param INT $idPackage id for the package
      * @param INT $idPrice   id for the price
      * @return array action message
      */
     public function addToCart($idPackage, $idPrice, $options){
         global $database, $user;
         $data = [];

         if(!$idPackage || !$idPrice){
             $err_mes = [
                 'code' => 'error',
                 'message' => 'INVALID_SELECTION'
             ];
             $data['messages'][] = $err_mes;

             return $data;
         }

         $idPackage = $database->escapeValue($idPackage);
         $idPrice = $database->escapeValue($idPrice);
         $options = json_decode($options);

         $idUser = $user->getUserId();
         $sql = "SELECT  plcl.idCommercialLead, availableCl.idCustomerInstance
                 FROM ".TABLES['price_list_commercial_lead']." plcl
                 INNER JOIN
                 (
                     SELECT rclc.idCommercialLead, rclc.id as idCustomerInstance
                     FROM ".TABLES['customers']." cust
                     INNER JOIN ".TABLES['rel_commercial_lead_customers']." rclc
                     ON cust.id=rclc.idCustomer AND rclc.isLinkEnabled=1
                     WHERE cust.idUser=$idUser
                 ) availableCl
                 ON availableCl.idCommercialLead = plcl.idCommercialLead
                 WHERE plcl.id=$idPrice
                 LIMIT 1";
         $customer = $database->fetchResultArray($sql);

         if(count($customer) !== 1){
             $err_mes = [
                 'code' => 'error',
                 'message' => 'INVALID_USER'
             ];
             $data['messages'][] = $err_mes;

             return $data;
         }

         $sql = "SELECT rclc.idCommercialLead
                FROM ".TABLES['rel_commercial_lead_customers']." rclc
                INNER JOIN ".TABLES['web_shop_cart']." wsc
                    ON wsc.idCustomerInstance=rclc.id
                INNER JOIN ".TABLES['customers']." c
                    ON c.id=rclc.idCustomer
                WHERE c.idUser=$idUser
                LIMIT 1
                ";
         $alreadySelectedCL = $database->fetchResultArray($sql);
         if(count($alreadySelectedCL) > 0 && $alreadySelectedCL[0]['idCommercialLead'] !== $customer[0]['idCommercialLead']){
             $err_mes = [
                 'code' => 'error',
                 'message' => 'ONLY_ONE_CL'
             ];
             $data['messages'][] = $err_mes;

             return $data;
         }

         $sqlCheckPackage = "
                    SELECT idPackage
                        FROM ".TABLES['web_shop_cart']."
                    WHERE idPackage = $idPackage
                        AND idCustomerInstance = ".$customer[0]['idCustomerInstance']."
                        AND idUser = $idUser
        ";
         $result = $database->query($sqlCheckPackage);
         if($database->numRows($result) > 0){
             $err_mes = [
                 'code' => 'error',
                 'message' => 'PACKAGE_ALREADY_IN_CART'
             ];
             $data['messages'][] = $err_mes;

             return $data;
         }

         $sqlPackageInstance = "SELECT MAX(rpp.packageInstance) as maxInstance
                                FROM ".TABLES['rel_package_products']." rpp
                                WHERE rpp.idPackage=$idPackage";
         $result = $database->fetchResultArray($sqlPackageInstance);
         if(count($result) === 0){
             $err_mes = [
                 'code' => 'error',
                 'message' => 'PACKAGE_ERROR'
             ];
             $data['messages'][] = $err_mes;

             return $data;
         }
         $maxPackageInstance = $result[0]['maxInstance'];

         $sqlIns = "INSERT INTO ".TABLES['web_shop_cart']."
                    (idPackage, idCustomerInstance, idPrice, idUser, quantity, packageInstance)
                    VALUES($idPackage,
                        ".$customer[0]['idCustomerInstance'].",
                        $idPrice,
                        $idUser,
                        1,
                        $maxPackageInstance)";

         $query = $database->query($sqlIns);
         $idCart = $database->getInsertId();

         if($database->affectedRows() !== 1){

             $err_mes = [
                 'code' => 'error',
                 'message' => 'PACKAGE_ALREADY_IN_CART'
             ];
             $data['messages'][] = $err_mes;

             return $data;
         }

         $insertedOptions = $this->insertOptionsToCart($idCart, $options);

         if($insertedOptions){
             $mes = [
                 'code' => 'success',
                 'message' => 'OPTIONS_ADDED'
             ];
             $data['messages'][] = $mes;
         }

         $mes = [
             'code' => 'success',
             'message' => 'PACKAGE_ADDED'
         ];
         $data['messages'][] = $mes;

         return $data;
     }

     /**
      * update the quantity in the car for an item
      * @param  INT $idPackage          id for the package
      * @param  INT $idCustomerInstance id for the customer instance based on linking to cl
      * @param  INT $idPrice            id for the price
      * @param  INT $quantity           quantity value
      * @return array                   update message
      */
     public function updateQuantity($idPackage, $idCustomerInstance, $idPrice, $quantity){
         global $database, $user;
         $data = [];

         $idPackage = $database->escapeValue($idPackage);
         $idPrice = $database->escapeValue($idPrice);
         $idCustomerInstance = $database->escapeValue($idCustomerInstance);
         $quantity = $database->escapeValue($quantity);

         if($database->invalidNumber('QUANTITY', $quantity, 1, 100)){
             $err_mes = [
                 'code' => 'error',
                 'message' => 'INVALID_QUANTITY'
             ];
             $data['messages'][] = $err_mes;

             return $data;
         }

         $sqlUpd = "UPDATE ".TABLES['web_shop_cart']."
                SET quantity=$quantity
                WHERE idPackage=$idPackage
                    AND idPrice=$idPrice
                    AND idCustomerInstance=$idCustomerInstance
                    AND idUser=".$user->getUserId()."
                ";
         $query = $database->query($sqlUpd);

         if($database->affectedRows() !== 1){
             $err_mes = [
                 'code' => 'error',
                 'message' => 'NO_CHANGE'
             ];
             $data['messages'][] = $err_mes;

             return $data;
         }

         $mes = [
             'code' => 'success',
             'message' => 'QUANTITY_UPDATED'
         ];
         $data['messages'][] = $mes;

         return $data;
     }

     /**
      * remove item from cart
      * @param  INT $idCart          id for the cart
      * @return array                update message
      */
     public function removeFromCart($idCart){
         global $database, $user;
         $data = [];

         $idCart = $database->escapeValue($idCart);

         $sql = "DELETE FROM ".TABLES['web_shop_cart_extra_packages']."
                 WHERE idCart=$idCart ";
         $query = $database->query($sql);

         $sql = "DELETE FROM ".TABLES['web_shop_cart']."
                    WHERE id=$idCart
                    AND idUser=".$user->getUserId();

         $query = $database->query($sql);
         if($database->affectedRows() !== 1){
             $err_mes = [
                 'code' => 'error',
                 'message' => 'INVALID_PACKAGE_FOR_REMOVE'
             ];
             $data['messages'][] = $err_mes;
         }else{
             $message = [
                 'code' => 'success',
                 'message' => 'PACKAGE_REMOVED_FROM_CART'
             ];
             $data['messages'][] = $message;
         }

         return $data;
     }

     /**
      * get count of items in the cart
      * @return Object number of items in the cart
      */
     public function getShopCartCount(){
         global $database, $user;

         $sql = "SELECT COUNT(idPackage) as newShopCartItemsCount
                 FROM ".TABLES['web_shop_cart']."
                 WHERE idUser=".$user->getUserId();

        $data = $database->fetchResultArray($sql);

        return  !empty($data) ? $data[0] : [];
     }

     /**
      * get details of the customer logged in
      * @return Array with neccessary details
      */
     public function getCustomerDetails() {
         global $database, $user;
         $userId = $user->getUserId();
         $data = [];

         $sqlVatCode = "
                SELECT c.vatCode
                    FROM ".TABLES['customers']." c
                WHERE c.idUser = ".$userId."
                LIMIT 1";
        $result = $database->fetchResultArray($sqlVatCode);
        $data['vat'] = $result ? $result[0]['vatCode'] : '';

         if($result = $this->getDeliveryAddress()) {
             $data['delivery'] = $result[0];
         }

         if($result = $this->getBillingAddress()) {
             $data['billing'] = $result[0];
         }

         $data['countryNames']['delivery'] = $this->getCountryName($data, 'delivery');
         $data['countryNames']['billing'] = $this->getCountryName($data, 'billing');

         return $data;
     }

     /**
      * returns the delivery address information for the user logged in
      * @return Array delivery address
      */
     private function getDeliveryAddress() {
         global $database, $user;

         $sqlDelivery = "
                SELECT
                  da.detailedAddress AS detailedAddress,
                  da.city AS city,
                  da.zip AS zipCode,
                  co.id AS idCountrySelected
                FROM
                  ".TABLES['customers']." c
                  INNER JOIN ".TABLES['delivery_addresses']." da
                    ON da.idUser = c.idUser
                  INNER JOIN ".TABLES['countries']." co
                    ON co.id = da.idCountry
                WHERE c.idUser = ".$user->getUserId()."
                LIMIT 1
         ";

         return $database->fetchResultArray($sqlDelivery);
     }

     /**
      * returns the billing address information for the user logged in
      * @return Array billing address info
      */
     private function getBillingAddress() {
         global $database, $user;

         $sqlBillingAddress = "
                SELECT
                  bi.company AS companyName,
                  bi.firstName AS firstName,
                  bi.lastName AS lastName,
                  bi.idCountry AS idCountrySelected,
                  bi.detailedAddress AS detailedAddress,
                  bi.city AS city,
                  bi.zip AS zipCode
                FROM
                  ".TABLES['customers']." c
                  INNER JOIN ".TABLES['billing_information']." bi
                    ON bi.idUser = c.idUser
                WHERE c.idUser = ".$user->getUserId()."
                LIMIT 1
         ";

         return $database->fetchResultArray($sqlBillingAddress);
     }

     /**
      * gets the country name for the id provided
      * @param  Array $data          all details
      * @param  String $addressType  delivery or billing address
      * @return String               name of the country
      */
     private function getCountryName($data, $addressType) {
         if(array_key_exists($addressType, $data)) {
             return $this->getCountryDetailsById($data[$addressType]['idCountrySelected'])[0]['countryName'];
         }

         return '';
     }

     /**
      * get all the countries
      * @return Array country names and ids
      */
     public function getCountries() {
         global $database;

         $sql = "SELECT c.id, c.name
                  FROM ".TABLES['countries']." c";

        return $database->fetchResultArray($sql);
     }

     /**
      * genereate the order number using the id
      * @param  INT $idOrder id of the order
      * @return INT          order number containg the id value
      */
     private function generateOrderNumber($idOrder){

         return 1000000000 + $idOrder;
     }

     private function addOrderDocuments($idOrder, $idCustomerInstance){
         global $database, $user;
         $getDocSql = "SELECT wsc.idDocument, wsc.idAgreementDocument, wsc.idPackage
                        FROM ".TABLES['web_shop_cart']." wsc
                        WHERE idCustomerInstance=$idCustomerInstance";
         $query = $database->query($getDocSql);
         $vals = "";
         while($row = $database->fetchArray($query)){
             if($row['idDocument']){
                 $vals .= "(" . $idOrder . ", " . $row['idPackage'] . ", " . $row['idDocument'] . "),";
             }

             if($row['idAgreementDocument']){
                 $vals .= "(" . $idOrder . ", " . $row['idPackage'] . ", " . $row['idAgreementDocument'] . "),";
             }
         }
         if($vals){
             $vals = rtrim($vals, ',');
             $sql = "INSERT INTO ".TABLES['rel_order_documents']."
                    (idOrder, idPackage, idDocument)
                    VALUES $vals";
             $query = $database->query($sql);

             return $database->affectedRows();
         }

         return 1;
     }

     /**
      * add extra option for order packages
      * @param INT $idOrder id for the ORDER
      * @return INT         number of inserted elements
      */
     private function addOrderExtraPackages($idOrder){
         global $database, $user;

         $sql = "INSERT INTO ".TABLES['rel_order_extra_packages']."
                (idOrder, idPackage, idExtraPackage)
                SELECT $idOrder, wsc.idPackage, wscep.idExtraPackage
                FROM ".TABLES['web_shop_cart']." wsc
                INNER JOIN ".TABLES['web_shop_cart_extra_packages']." wscep
                    ON wsc.id=wscep.idCart
                WHERE wsc.idUser=".$user->getUserId();
         $query = $database->query($sql);

         return $database->affectedRows();
     }

     private function calculatePackageTotalPrice($cartPackage){
         $totalPrice = [
             'fixedPrice' => 0,
             'recurrentPrice' => 0,
             'servicesPrice' => 0
         ];

         $totalPrice['fixedPrice'] += $cartPackage['fixedPrice'];
         $totalPrice['recurrentPrice'] += $cartPackage['recurentPrice'];
         $totalPrice['servicesPrice'] += $cartPackage['servicesPrice'];
         if(isset($cartPackage['options'])){
             foreach ($cartPackage['options'] as $option) {
                 $totalPrice['fixedPrice'] += $option->fixedPrice;
                 $totalPrice['recurrentPrice'] += $option->recurrentPrice;
                 $totalPrice['servicesPrice'] += $option->servicesPrice;
             }
         }

         if(isset($cartPackage['additionalPackages'])){
             foreach ($cartPackage['additionalPackages'] as $additionalPackage) {
                 $totalPrice['fixedPrice'] += $additionalPackage->fixedPrice;
                 $totalPrice['recurrentPrice'] += $additionalPackage->recurrentPrice;
                 $totalPrice['servicesPrice'] += $additionalPackage->servicesPrice;
             }
         }

         return $totalPrice;
     }

     /**
      * adds the order with the whole details
      * @param  String $cartPackages Json string with object containing the packages to order
      * @param  String $deliveryInfo Json string with object containing the delivery informations
      * @param  String $billingInfo  Json string with object containing the billing informations
      * @param  String $details      Json string with object containing the details like refenrence or tender numbers
      * @return Array               confirmation message
      */
     public function placeOrder($cartPackages, $deliveryInfo, $billingInfo, $details) {
        global $database, $user;
        $rowsAffected = 0;
        $deletedFromCart = 0;
        $addressInserted = 0;
        $totalFixedPrice = 0;
        $totalServicePrice = 0;
        $userId = $user->getUserId();
        $newCartPackages = [];
        $packageToDisplay = '';

        $cartPackages = (array) json_decode($cartPackages);
        $deliveryInfo = (array) json_decode($deliveryInfo);
        $billingInfo = (array) json_decode($billingInfo);
        $details = (array) json_decode($details);

        foreach($cartPackages as $packKey => $packageDetails) {
            $packageDetails = (array) $packageDetails;
            foreach($packageDetails as $packDetailKey => $packageDetail) {
                if(!is_array($packageDetail)){
                    $newCartPackages[$packKey][$packDetailKey] = $database->escapeValue($packageDetail);
                }else{
                    $newCartPackages[$packKey][$packDetailKey] = $packageDetail;
                }
            }
            $commercialLeadName = $packageDetails['commercialLead'];
        }

        $cartPackages = $newCartPackages;
        foreach ($cartPackages as $position => $cartPackage) {
            $servicesPrice = $cartPackage['recurentPrice'] + $cartPackage['servicesPrice'];
            $optionsToDisplay = '';
            if(isset($cartPackage['options'])){
                $optionsToDisplay .= '(';
                foreach ($cartPackage['options'] as $key => $option) {
                    $optionsToDisplay .= $option->groupName . ' : ' . $option->packageName .',';
                }
                $optionsToDisplay = rtrim($optionsToDisplay, ',');
                $optionsToDisplay .= ')';
            }

            if(isset($cartPackage['additionalPackages'])){
                $optionsToDisplay .= '(';
                foreach ($cartPackage['additionalPackages'] as $key => $option) {
                    $optionsToDisplay .= $option->packageName .', ';
                }
                $optionsToDisplay = rtrim($optionsToDisplay, ',');
                $optionsToDisplay .= ')';
            }
            $packageToDisplay .= ($position + 1).'. <b>'.$cartPackage['packageName'].'</b>
                                    '.$optionsToDisplay.'
                                    <div class="mail-order-package-detail">
                                        <div>Commercial lead: '.$cartPackage['commercialLead'].'</div>
                                        <div>Payment type: '.$cartPackage['payType'].'</div>
                                        <div>Quantity: '.$cartPackage['quantity'].'</div>
                                        <label>'.$cartPackage['fixedPrice'].' ('.$servicesPrice.' / '.$cartPackage['periodUnit'].')</label>
                                    </div> <br /><hr>
            ';
            $packageTotalPrices = $this->calculatePackageTotalPrice($cartPackage);

            $totalFixedPrice += $packageTotalPrices['fixedPrice'] * $cartPackage['quantity'];
            $totalServicePrice += ($packageTotalPrices['recurrentPrice'] + $packageTotalPrices['servicesPrice']) * $cartPackage['quantity'];
            $totalPeriodUnit = $cartPackage['periodUnit'];
        }
        $packageToDisplay .= 'Total Price: <b>'.$totalFixedPrice.' ('.$totalServicePrice.' / '.$totalPeriodUnit.')</b><br /><br />';

        foreach($deliveryInfo as $deliveryKey => $deliveryDetail) {
            $deliveryInfo[$deliveryKey] = $database->escapeValue($deliveryDetail);
        }

        foreach($billingInfo as $billingKey => $billingDetail) {
            $billingInfo[$billingKey] = $database->escapeValue($billingDetail);
        }

        foreach($details as $detailKey => $detail) {
            $details[$detailKey] = $database->escapeValue($detail);
        }

        if($data['messages'][] = $this->validateOrderDetails($cartPackages, $deliveryInfo, $billingInfo, $details)) {
           return $data;
       }

       $database->beginTransaction();

       $sqlCustomerInstance = "
            SELECT
                rclc.id AS idCustomerInstance
            FROM
                ".TABLES['rel_commercial_lead_customers']." rclc
            INNER JOIN ".TABLES['customers']." c
                ON c.id = rclc.idCustomer
                AND c.idUser = $userId
            INNER JOIN ".TABLES['commercial_leads']." cl
                ON cl.id = rclc.idCommercialLead
                AND cl.name = '".$commercialLeadName."'
            WHERE rclc.isLinkEnabled=1
            LIMIT 1";
        $result = $database->fetchResultArray($sqlCustomerInstance);
        if($result && $result[0]['idCustomerInstance']) {
            $idCustomerInstance = $result[0]['idCustomerInstance'];
        } else {
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'LINK_CUSTOMER_CL'
            ];
            $database->rollback();

            return $data;
        }

        $sqlOrderNumber = "
                SELECT
                  MAX(id) + 1 AS orderNumber
                FROM
                  ".TABLES['orders']."
                LIMIT 1";
        $result = $database->fetchResultArray($sqlOrderNumber);
        $orderNumber = $result && $result[0]['orderNumber'] ? $this->generateOrderNumber($result[0]['orderNumber']) : '1000000000';

        $today = new DateTime();
        $reference = array_key_exists('reference', $details) && $details['reference'] ? $details['reference'] : '';
        $tender = array_key_exists('tender', $details) && $details['tender'] ? $details['tender'] : '';
        $countryDetails = $this->getCountryDetailsById($deliveryInfo['idCountrySelected'])[0];
        $countryName = $countryDetails['countryName'];
        $countryCode = $countryDetails['countryCode'];
        $concatenatedDeliveryAddress = $deliveryInfo['detailedAddress'].", ".$deliveryInfo['city'].", $countryName, ".$deliveryInfo['zipCode'];
        $billingCountryDetails = $this->getCountryDetailsById($billingInfo['idCountrySelected'])[0];
        $concatenatedBillingAddress = $billingInfo['detailedAddress'].", ".$billingInfo['city'].", ".$billingCountryDetails['countryName'].", ".$billingInfo['zipCode'];
        $projectNumber = $countryCode . $orderNumber;
        $orderDate = $today->format('Y-m-d H:m:s');

        $packageToDisplay .= '<b>Delivery address</b>: '.$concatenatedDeliveryAddress.'<br />';
        $packageToDisplay .= '<b>Billing address</b>: '.$concatenatedBillingAddress.'<br />';

        $sql = "SELECT MAX(t.id) as idTemrs
                FROM ".TABLES['terms']." t";
        $terms = $database->fetchResultArray($sql)[0];

        $sql = "
            INSERT INTO ".TABLES['orders']."
                (idCustomerInstance, orderNumber, orderDate, reference, tender, projectNumber, deliveryAddress, billingAddress, idTerms)
            VALUES (
                    $idCustomerInstance,
                    '".$orderNumber."',
                    '".$orderDate."',
                    '".$reference."',
                    '".$tender."',
                    '".$projectNumber."',
                    '".$concatenatedDeliveryAddress."',
                    '".$concatenatedBillingAddress."',
                    '".$terms['idTemrs']."'
                )
        ";

        $query = $database->query($sql);

        if($database->affectedRows() === 1){
            $rowsAffected++;
        } else {
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'ERROR_ORDER_INSERT'
            ];
            $database->rollback();

            return $data;
        }

        $idOrder = $database->getInsertId();

        if($this->addOrderPackageRelation($cartPackages, $idOrder) > 0) {
            $rowsAffected++;
        } else {
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'ERROR_ORDER_PACK_RELATION'
            ];
            $database->rollback();

            return $data;
        }

        if($this->addUpdateOrderDeliveryAddress($deliveryInfo) > 0) {
            $addressInserted++;
        }

        if($this->addUpdateOrderBillingAddress($billingInfo) > 0) {
            $addressInserted++;
        }

        if($this->addOrderDocuments($idOrder, $idCustomerInstance) > 0) {
            $rowsAffected++;
        } else {
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'ERROR_ON_ADDING_DOCUMENTS'
            ];
            $database->rollback();

            return $data;
        }

        $addedOptions = $this->addOrderExtraPackages($idOrder);
        $deletedFromCart = $this->removePackagesFromCartAfterOrder($idCustomerInstance, $userId);
        $data['messages'][] = $this->addInstallationCompanySelection($cartPackages, $idOrder);

        if($rowsAffected >= 3) {
            $database->commit();
            $data['messages'][] = [
                'code' => 'success',
                'message' => 'ORDER_PLACED'
            ];

            if($addressInserted > 0) {
                $data['messages'][] = [
                    'code' => 'success',
                    'message' => 'ADDRESS_INSERTED'
                ];
            }

            if($deletedFromCart > 0) {
                $data['messages'][] = [
                    'code' => 'success',
                    'message' => 'PACKAGES_DELETED'
                ];
            }

            $orderInfo['idOrder'] = $idOrder;
            $orderInfo['orderNumber'] = $orderNumber;
            $orderInfo['orderDate'] = $orderDate;

            $data['messages'][] = UtilsModel::sendOrderConfirmationMail($packageToDisplay, 'customer', $orderInfo);
            $data['messages'][] = UtilsModel::sendOrderConfirmationMail($packageToDisplay, 'broker', $orderInfo);
        } else {
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'ORDER_ERROR'
            ];
            $database->rollback();
        }

        return $data;
    }

    /**
     * checks whether the informations for the order are correct
     * @param  Array $cartPackages the packages to be placed in the order
     * @param  Array $deliveryInfo the delivery information for the order
     * @param  Array $billingInfo  the billing information for the order
     * @return Array               error message ot empty
     */
    private function validateOrderDetails($cartPackages, $deliveryInfo, $billingInfo, $details) {
       global $database;

       if(count($cartPackages) < 1) {
           return [
               'code' => 'error',
               'message' => 'CART_EMPTY'
           ];
       }

       if(count($deliveryInfo) < 1) {
           return [
               'code' => 'error',
               'message' => 'NO_DELIVERY_ADDRESS'
           ];
       }

       if((!array_key_exists('detailedAddress', $deliveryInfo) || $deliveryInfo['detailedAddress'] === '') ||
           (!array_key_exists('idCountrySelected', $deliveryInfo) || $deliveryInfo['idCountrySelected'] === '') ||
           (!array_key_exists('zipCode', $deliveryInfo) || $deliveryInfo['zipCode'] === '')) {

            return [
                'code' => 'error',
                'message' => 'INCOMPLETE_DELIVERY_ADDRESS'
            ];
        }

        if((!array_key_exists('companyName', $billingInfo) || $billingInfo['companyName'] === '') ||
            (!array_key_exists('firstName', $billingInfo) || $billingInfo['firstName'] === '') ||
            (!array_key_exists('lastName', $billingInfo) || $billingInfo['lastName'] === '') ||
            (!array_key_exists('detailedAddress', $billingInfo) || $billingInfo['detailedAddress'] === '') ||
            (!array_key_exists('idCountrySelected', $billingInfo) || $billingInfo['idCountrySelected'] === '') ||
            (!array_key_exists('zipCode', $billingInfo) || $billingInfo['zipCode'] === '')) {

             return [
                 'code' => 'error',
                 'message' => 'INCOMPLETE_BILLING_ADDRESS'
             ];
         }

       $checkMessage = $database->invalidLength('INVALID_LENGTH_DELIVERY_ADDRESS', $deliveryInfo['detailedAddress'], 500);
       if($checkMessage){
           return $checkMessage;
       }

       $checkMessage = $database->invalidLength('INVALID_LENGTH_BILLING_ADDRESS', $billingInfo['detailedAddress'], 500);
       if($checkMessage){
           return $checkMessage;
       }

       $checkMessage = $database->invalidLength('INVALID_LENGTH_CITY', $deliveryInfo['city'], 100);
       if($checkMessage){
           return $checkMessage;
       }

       $checkMessage = $database->invalidLength('INVALID_LENGTH_CITY', $billingInfo['city'], 100);
       if($checkMessage){
           return $checkMessage;
       }

      $checkMessage = $database->invalidLength('INVALID_LENGTH_ZIP', $deliveryInfo['zipCode'], 20);
      if($checkMessage){
          return $checkMessage;
      }

       $checkMessage = $database->invalidLength('INVALID_LENGTH_COMPANY_NAME', $billingInfo['companyName'], 300);
       if($checkMessage){
           return $checkMessage;
       }

       $checkMessage = $database->invalidLength('INVALID_LENGTH_FIRST_NAME', $billingInfo['firstName'], 200);
       if($checkMessage){
           return $checkMessage;
       }

       $checkMessage = $database->invalidLength('INVALID_LENGTH_LAST_NAME', $billingInfo['lastName'], 200);
       if($checkMessage){
           return $checkMessage;
       }

       $checkMessage = $database->invalidLength('INVALID_LENGTH_ZIP', $billingInfo['zipCode'], 20);
       if($checkMessage){
           return $checkMessage;
       }

       return [];
   }

   /**
    * adds the installation company in the selection relations table
    * @param Array $cartPackages  the packages from the cart
    * @param Int   $idOrder       the id of the order
    * @return Array with confirmation message
    */
   private function addInstallationCompanySelection($cartPackages, $idOrder) {
       global $database;
       $message = [];

       foreach($cartPackages as $position => $package) {
           $sql = "
               SELECT
                  rpp.idProduct
                FROM
                  ".TABLES['rel_package_products']." rpp
                  INNER JOIN ".TABLES['suppliers_countries_products']." scp
                    ON scp.idProduct = rpp.idProduct
                  INNER JOIN ".TABLES['product_categories']." pc
                    ON scp.idProductCategory = pc.id
                WHERE pc.id = ".self::PRODUCT_CATEGORIES['INSTALLATION']."
                  AND rpp.idPackage = ".$package['idPackage']."
                  AND rpp.packageInstance = ".$package['packageInstance'];
            $products = $database->fetchResultArray($sql);
            if(count($products) === 1) {
                $installationScheduling = new InstallationScheduling();
                $message = $installationScheduling->changeInstallationCompany($idOrder, $package['idPackage'], $products[0]['idProduct']);
            }
       }

       return $message;
   }

   /**
    * returns the name and code of the country by it's id
    * @param  int $idCountry id of the country
    * @return string            the name of the country
    */
   public function getCountryDetailsById($idCountry) {
       global $database;
       $countryName = '';

       $sql = "
                SELECT name AS countryName,
                    UPPER(code) AS countryCode
                    FROM ".TABLES['countries']."
                WHERE id=$idCountry
                LIMIT 1";
        return $database->fetchResultArray($sql);
   }

   /**
    * adds or updates the delivery address for the user logged in
    * @param Array $deliveryInfo contaions the delivery address
    * @return Int affected rows
    */
   private function addUpdateOrderDeliveryAddress($deliveryInfo) {
       global $database, $user;

       $sql = "
            SELECT
             id
            FROM
             ".TABLES['delivery_addresses']."
            WHERE idUser = ".$user->getUserId()."
             AND idCountry = ".$deliveryInfo['idCountrySelected']."
             AND city = '".$deliveryInfo['city']."'
             AND detailedAddress = '".$deliveryInfo['detailedAddress']."'
             AND zip = '".$deliveryInfo['zipCode']."'
       ";
       $query = $database->query($sql);
       if($database->numRows($query) === 0) {
           $sqlDeliveryAddress = "
                   INSERT INTO ".TABLES['delivery_addresses']."
                       (idUser, idCountry, city, detailedAddress, zip)
                       VALUES (
                           ".$user->getUserId().",
                           ".$deliveryInfo['idCountrySelected'].",
                           '".$deliveryInfo['city']."',
                           '".$deliveryInfo['detailedAddress']."',
                           '".$deliveryInfo['zipCode']."'
                       )
           ";
           $query = $database->query($sqlDeliveryAddress);

           return $database->affectedRows();
       }

      return 0;
   }

   /**
    * adds or updates the billing information for the user
    * @param Array $billingInfo array of billing info - names and address
    * @return Int affected rows
    */
   private function addUpdateOrderBillingAddress($billingInfo) {
       global $database, $user;

       $sql = "
            SELECT
             id
            FROM
             ".TABLES['billing_information']."
            WHERE idUser = ".$user->getUserId()."
             AND idCountry = ".$billingInfo['idCountrySelected']."
             AND company = '".$billingInfo['companyName']."'
             AND firstName = '".$billingInfo['firstName']."'
             AND lastname = '".$billingInfo['lastName']."'
             AND city = '".$billingInfo['city']."'
             AND detailedAddress = '".$billingInfo['detailedAddress']."'
             AND zip = '".$billingInfo['zipCode']."'
       ";
       $query = $database->query($sql);
       if($database->numRows($query) === 0) {
           $sqlBillingAddress = "
                   INSERT INTO ".TABLES['billing_information']."
                       (idUser, idCountry, company, firstName, lastName, city, detailedAddress, zip)
                       VALUES (
                           ".$user->getUserId().",
                           ".$billingInfo['idCountrySelected'].",
                           '".$billingInfo['companyName']."',
                           '".$billingInfo['firstName']."',
                           '".$billingInfo['lastName']."',
                           '".$billingInfo['city']."',
                           '".$billingInfo['detailedAddress']."',
                           '".$billingInfo['zipCode']."'
                       )
           ";
           $query = $database->query($sqlBillingAddress);

           return $database->affectedRows();
       }

       return 0;
   }

   /**
    * adds the relation between order and packages
    * @param Array $orderPackages all the details for packages in order
    * @param Int $idOrder       the order id inserted
    * @return Int               the number of packages inserted
    */
   private function addOrderPackageRelation($orderPackages, $idOrder) {
       global $database;
       $insertValues = '';

       $sql = "
           INSERT INTO ".TABLES['rel_order_packages']."
               (idOrder, idPackage, packageInstance, idPaymentTerm, units, packageFixedPrice, packageRecuringPrice, packageServicePrice)
           VALUES
       ";

       foreach($orderPackages as $packageInfo) {
           $totalPackagePrice = $this->calculatePackageTotalPrice($packageInfo);
           $sqlPaymentType = "
                    SELECT id
                        FROM ".TABLES['payment_types']." pt
                    WHERE pt.payType='".$packageInfo['payType']."'";
           $result = $database->fetchResultArray($sqlPaymentType);
           $idPayType = $result && $result[0]['id'] ? $result[0]['id'] : 0;

           $insertValues .= "(
                                $idOrder,
                               '".$packageInfo['idPackage']."',
                               '".$packageInfo['packageInstance']."',
                               ".$packageInfo['idPayType'].",
                               '".$packageInfo['quantity']."',
                               '".$totalPackagePrice['fixedPrice']."',
                               '".$totalPackagePrice['recurrentPrice']."',
                                '".$totalPackagePrice['servicesPrice']."'
                           ),";
       }

       $insertValues = $insertValues ? rtrim($insertValues, ',') : '';
       if($insertValues) {
           $sql .= $insertValues;
           $query = $database->query($sql);

           return $database->affectedRows();
       }

      return 0;
   }

   /**
    * remove the packages from the web shop cart after placing the order
    * @param  Int $idCustomerInstance id of the customer
    * @return Int                     number of rows deleted
    */
   private function removePackagesFromCartAfterOrder($idCustomerInstance) {
       global $database;

       $sql = "
            DELETE
            FROM
              ".TABLES['web_shop_cart']."
            WHERE idCustomerInstance = $idCustomerInstance";

        $result = $database->query($sql);

        return $database->affectedRows();
   }

   /**
    * upload questionaires for placing an order
    * @param  Array $file         uploaded file
    * @param  String $idDocumentType type of the document
    * @param  String $documentName the name to be set for the file in database
    * @param  INT $idPackage    id for the package
    * @return Array               upload message
    */
   public function uploadOrderDocument($file, $idDocumentType, $idPackage){
       global $database, $user;
       $isReUpload = false;

       $documentName = 'customerQuestionaire_'.$idPackage.'_'.date('Y_m_d');

       $fileManager = new FileManager();
       $documentField = '';
       if(intval($idDocumentType) === self::DOCUMENT_TYPES['ID_CUSTOMER_QUESTIONNAIRE']){
           $documentField = 'idDocument';
       }else{
           $documentField = 'idAgreementDocument';
       }

       $sql = "SELECT $documentField AS idDocument
               FROM ".TABLES['web_shop_cart']."
               WHERE idUser=".$user->getUserId()." AND idPackage=$idPackage AND $documentField IS NOT NULL";

       $documents = $database->fetchResultArray($sql);
       if(count($documents) > 0){
           $document = $documents[0];
           $data = $fileManager->updateDocument($document['idDocument'], $file);
           $isReUpload = true;
       }else{
           $uploadedBy = $user->getUserId();
           $data = $fileManager->uploadFile($file, $idDocumentType, $documentName, $uploadedBy);
           $isReUpload = false;
       }

       if(isset($data['messages'])){
           return $data;
       }

       if(!$isReUpload){
           $idDocument = $data['idDocument'];

           $sql = "UPDATE ".TABLES['web_shop_cart']."
                   SET $documentField=$idDocument
                   WHERE idUser=".$user->getUserId()." AND idPackage=$idPackage";
           $query = $database->query($sql);

           if($database->affectedRows() > 0){
               $data['messages'][] = [
                   'code' => 'success',
                   'message' => 'FILE_UPLOADED'
               ];
           }else{
               $data['messages'][] = [
                   'code' => 'error',
                   'message' => 'NOT_LINKED_TO_CART'
               ];
           }
       }else{
           $data['messages'][] = [
               'code' => 'success',
               'message' => 'FILE_UPLOADED'
           ];
       }

       return $data;
   }

   /**
    * get files that have been uploaded before palcing an order and are found in the cart
    * @param  INT $packages id of the package
    * @return Array         array of uploaded documetns for a package
    */
   private function getUploadedFilesForOrder($packages){
       global $database, $user;
       $data = [
           'questionnaire' => [],
           'agreement' => []
       ];

       $sql = "SELECT
                   wsc.idPackage,
                   wsc.idDocument AS idQuestionnaire,
                   wsc.idAgreementDocument AS idAgreement
               FROM ".TABLES['documents']." doc
               INNER JOIN ".TABLES['web_shop_cart']." wsc
                   ON doc.id=wsc.idDocument AND wsc.idUser=doc.uploadedBy
               WHERE wsc.idPackage IN($packages) AND wsc.idUser=".$user->getUserId()."";
       $query = $database->query($sql);
       while($row = $database->fetchArray($query)){
           if($row['idQuestionnaire']){
              $data['questionnaire'][] = $row['idPackage'];
           }

           if($row['idAgreement']){
              $data['agreement'][] = $row['idPackage'];
           }
       }

       return $data;
   }

   /**
    * get required documents to place an order
    * @param  Array $packages ids for the packages in the cart
    * @return Array           array of documents
    */
   public function getCartDocuments($packages){
       global $database;

       $data = [];
       $packages = json_decode($packages);
       $packages = implode(',', $packages);
       $packages = $database->escapeValue($packages);

       $uploaded = $this->getUploadedFilesForOrder($packages);

       $sql = "SELECT
                    d.id,
                    d.documentName,
                    d.documentPath,
                    d.extension,
                    p.id AS idPackage,
                    p.name AS packageName,
                    d.idDocumentType
                FROM  ".TABLES['documents']." d
                INNER JOIN ".TABLES['rel_package_documents']." rpd
                    ON rpd.idDocument=d.id
                INNER JOIN ".TABLES['packages']." p
                    ON p.id=rpd.idPackage
                WHERE rpd.idPackage IN($packages)
                    AND d.idDocumentType IN(".self::DOCUMENT_TYPES['ID_TEMPLATE_QUESIONNAIRE'].", ".self::DOCUMENT_TYPES['ID_TEMPLATE_AGREEMENT'].")";

       $query = $database->query($sql);
       while($row = $database->fetchArray($query)){
           $currentTypeUploaded = intval($row['idDocumentType']) === self::DOCUMENT_TYPES['ID_TEMPLATE_QUESIONNAIRE'] ? $uploaded['questionnaire'] : $uploaded['agreement'];
           $row['isUploaded'] = in_array($row['idPackage'], $currentTypeUploaded);
           $data[$row['idDocumentType']][] = $row;
       }

       return $data;
   }
 }
