<?php
class PackagesModel{
    const ID_PACKAGE_STANDARD_TYPE = 1;
    private $headersHelper;
    private $suppliersProducts;

    function __construct(){
        $this->headersHelper =  new HeadersHelper();
        $this->suppliersProducts = new SuppliersProducts();
    }

    private function getProcessesByCountry(){
        global $database;
        $data = [];

        $sql = "SELECT
                    proc.idCountry,
                    proc.name as processName
                FROM ".TABLES['processes']." proc";
        $query = $database->query($sql);
        while($row = $database->fetchArray($query)){
            $data[$row['idCountry']][] = $row;
        }

        return $data;
    }

    /**
     * Get the headers requried for the packages
     * @param  string $type type of the headere, posible values array or sql
     * @return string|array      returns the sql header or the array of for the headers
     */
    public function getPackagesHeaders( $type = 'array'){
        global $user;
        $headers = [
            'p.id' => 'id',
            'p.status' => 'status',
            'p.description' => 'description',
            'p.reference' => 'reference',
            'IFNULL(prices.idPackage, 0)' => 'isPriceSet',
            'p.name' => 'name',
            'pt.id' => 'idPackageType',
            'pt.packageType' => 'packageType',
            'c.name' => 'sellIn',
            'c.id' => 'idCountry',
            'c.code' => 'countryCode',
            'GROUP_CONCAT(CONCAT(rpp.quantity, \' x \', prod.productName))' => 'products',
            '\'-\'' => 'processes',
            'additionalInstallDays' => 'additionalInstallationDays',
            '\'\'' => 'hasDocuments'
        ];
        $data['headers'] = $this->headersHelper->getHeader($headers, $type);
        $data['userType'] = $user->getUserType();

        return $data;
    }

    /**
     * get the list of packages
     * @param $idCountry int - the id of the country selected
     * @return Array array of packages
     */
    public function getPackages($idCountry = 0){
        global $database, $user;
        $optionsManager = new PacakgeOptionsManager();
        $prices = new Prices();
        $packageDocuments = new PackageDocuments();
        $whereClause = "WHERE 1=1";
        $extraJoin = "";
        $data = [];

        $idCountry = $database->escapeValue($idCountry);
        if($idCountry) {
            $whereClause .= " AND c.id=$idCountry ";
        }

        $packageAvailablePrices = $prices->getPackagesAvailablePayTypes();
        $documents = $packageDocuments->getPackageDocuments();
        $extraPackages = $optionsManager->getExtraLinkedPackages();
        $processes = $this->getProcessesByCountry();

        if($user->getUserType() === USER_TYPES['BROKER']){
            $extraJoin = " LEFT OUTER JOIN
                           (SELECT DISTINCT plb.idPackage
                           FROM ".TABLES['price_list_broker']." plb
                           ) prices
                           ON prices.idPackage=p.id
                           ";
        }

        if($user->getUserType() === USER_TYPES['COMMERCIAL_LEAD']){
            $extraJoin = " INNER JOIN
                           (SELECT DISTINCT plb.idPackage
                           FROM ".TABLES['price_list_broker']." plb
                           LEFT OUTER JOIN
                            (SELECT DISTINCT myPackages.idPackage
                            FROM ".TABLES['price_list_commercial_lead']." myPackages
                            INNER JOIN ".TABLES['commercial_leads']." cl
                            ON cl.id=myPackages.idCommercialLead
                            WHERE cl.idUser=".$user->getUserId().") plcl
					       ON plcl.idPackage=plb.idPackage
                           WHERE plcl.idPackage IS NULL
                           ) prices
                           ON prices.idPackage=p.id
                           ";
            $whereClause .= " AND p.status='available'";
        }

        $headersSql = $this->getPackagesHeaders('sql')['headers'];
        $sql = "SELECT $headersSql
                    FROM ".TABLES['packages']." p
                INNER JOIN ".TABLES['package_types']." pt
                    ON pt.id=p.idPackageType
                INNER JOIN ".TABLES['countries']." c
                    ON c.id=p.idCountry
                INNER JOIN ".TABLES['rel_package_products']." rpp
                    ON p.id=rpp.idPackage
                INNER JOIN ".TABLES['suppliers_countries_products']." prod
                    ON prod.idProduct=rpp.idProduct
                INNER JOIN
                    (SELECT
                      rpp_last.idPackage,
                      MAX(rpp_last.packageInstance) AS maxInstance
                    FROM
                      ".TABLES['rel_package_products']." rpp_last
                    GROUP BY rpp_last.idPackage) last_instance
                    ON last_instance.idPackage = rpp.idPackage
                    AND last_instance.maxInstance = rpp.packageInstance
                $extraJoin
                $whereClause
                GROUP BY p.id
                ORDER BY p.name, p.id";
        $query = $database->query($sql);
        while($row = $database->fetchArray($query)){
            $row['documents'] = isset($documents[$row['id']]) ? $documents[$row['id']] : [];

            $row['processes'] = isset($processes[$row['idCountry']]) ? $processes[$row['idCountry']] : [];
            $row['prices'] = isset($packageAvailablePrices[$row['id']]) ? $packageAvailablePrices[$row['id']] : [];
            $row['extraPackages']['groups'] = isset($extraPackages['groups'][$row['id']]) ? $extraPackages['groups'][$row['id']] : [];
            $row['extraPackages']['additionalPackages'] = isset($extraPackages['additionalPackages'][$row['id']]) ? $extraPackages['additionalPackages'][$row['id']] : [];
            $data[] = $row;
        }

        return $data;
    }

    /**
     * get commercial lead prices
     * @return Array  price list for commercial lead
     */
    private function getCommercialLeadPriceList(){
        global $database, $user;
        $pricesHelper = new Prices();
        $interestRate = new InterestRate();
        $interestRateValue = $interestRate->getInterestRate()['interestRate'];
        $interestRateCustomers = $interestRate->getInterestRateForCustomers();
        $data['interestRate'] = $interestRateValue;
        $data['interestRateValues'] = $interestRateCustomers;

        $sql = "SELECT
                    c.id AS idCustomer,
                    plcl.idPackage,
                    pt.id AS idPaymentType,
                    pt.payType,
                    pt.packagePayPeriod,
                    pt.servicesContractPeriod,
                    pt.maxContractPeriod,
                    IFNULL(c.name, 'Default') as customer,
                    plcl.fixedExtra as fixedCommission,
                    plcl.recurentExtra as recurentCommission,
                    plcl.servicesExtra as recurentServicesCommission,
                    plcl.visibleToCustomer,
                    plb.minimalFixedPrice,
                    plb.minimalServicesPrice,
                    plb.principalAmount,
                    bcs.commercialLeadSplit
                FROM ".TABLES['price_list_commercial_lead']." plcl
                LEFT OUTER JOIN ".TABLES['customers']." c
                    ON c.id = plcl.idCustomer
                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
                INNER JOIN ".TABLES['broker_commission_split']." bcs
                    ON bcs.idPackage=plcl.idPackage
                INNER JOIN ".TABLES['commercial_leads']." cl
                    ON cl.id=plcl.idCommercialLead
                WHERE cl.idUser=".$user->getUserId();
        $query = $database->query($sql);
        $data = [];

        while($row = $database->fetchArray($query)){
            unset($row['commercialLeadSplit']);
            $interestRateCust = $row['idCustomer'] && isset($interestRateCustomers[$row['idCustomer']]) ?
                                floatval($interestRateCustomers[$row['idCustomer']]) :
                                $interestRateValue;
            $row['minimalRecurentPrice'] = $row['packagePayPeriod'] > 0
                ? $pricesHelper->PMT($interestRateCust / 100, $row['packagePayPeriod'], $row['principalAmount'])
                : 0;
            $row['minimalRecurentPrice'] = number_format($row['minimalRecurentPrice'], 2, '.', '');
            $brokerPrices[] = $row;

            $data[$row['idPackage']][$row['customer']][] = $row;
        }

        return $data;
    }

    /**
     * Get the headers requried for my packages
     * @param  string $type type of the headere, posible values array or sql
     * @return string|array      returns the sql header or the array of for the headers
     */
    public function getMyPackagesHeaders( $type = 'array'){
        global $user;

        $headers = [
            'p.id' => 'id',
            'p.status' => 'status',
            'p.name' => 'name',
            'pt.packageType' => 'packageType',
            'p.idPackageType' => 'idPackageType',
            'c.name' => 'sellIn',
            'c.id' => 'idCountry',
            'p.description' => 'description',
            '\'-\'' => 'processes',
            'GROUP_CONCAT(CONCAT(rpp.quantity, \' x \', prod.productName))' => 'products'
        ];

        $data['headers'] = $this->headersHelper->getHeader($headers, $type);
        $data['userType'] = $user->getUserType();
        return $data;
    }

    /**
     * get the list of my packages
     * @return Array array of my packages
     */
    public function getMyPackages($idCountry = 0){
        global $database, $user;
        $optionsManager = new PacakgeOptionsManager();
        $extraJoin = "";
        $whereClause = "WHERE 1=1";
        $prices = $this->getCommercialLeadPriceList();
        $extraPackages = $optionsManager->getExtraLinkedPackages();
        $processes = $this->getProcessesByCountry();

        $idCountry = $database->escapeValue($idCountry);
        if($idCountry) {
            $whereClause = " AND c.id=$idCountry ";
        }

        $headersSql = $this->getMyPackagesHeaders('sql')['headers'];
        $sql = "SELECT $headersSql
                FROM ".TABLES['packages']." p
                INNER JOIN ".TABLES['package_types']." pt
                    ON pt.id=p.idPackageType
                INNER JOIN ".TABLES['countries']." c
                    ON c.id=p.idCountry
                INNER JOIN ".TABLES['rel_package_products']." rpp
                    ON p.id=rpp.idPackage
                INNER JOIN
                    (SELECT
                      rpp_last.idPackage,
                      MAX(rpp_last.packageInstance) AS maxInstance
                    FROM
                      ".TABLES['rel_package_products']." rpp_last
                    GROUP BY rpp_last.idPackage
                    ) last_instance
                    ON last_instance.idPackage = rpp.idPackage
                    AND last_instance.maxInstance = rpp.packageInstance
                INNER JOIN ".TABLES['suppliers_countries_products']." prod
                    ON prod.idProduct=rpp.idProduct
                INNER JOIN
                (SELECT DISTINCT plcl.idPackage
                FROM ".TABLES['price_list_commercial_lead']." plcl
                INNER JOIN ".TABLES['commercial_leads']." cl
                    ON plcl.idCommercialLead=cl.id
                WHERE cl.idUser=" .$user->getUserId()." AND plcl.idCustomer IS NULL
                ) clprod
                    ON clprod.idPackage=p.id
                $whereClause
                GROUP BY p.id
                ORDER BY p.id, p.name";

        $data = [];
        $query = $database->query($sql);
        while($row = $database->fetchArray($query)){
            if(intval($row['idPackageType']) === 2){
                $row['processes'] = [];
            }else{
                $row['processes'] = isset($processes[$row['idCountry']]) ? $processes[$row['idCountry']] : [];
            }
            $row['prices'] = isset($prices[$row['id']]) ? $prices[$row['id']] : [];
            $row['extraPackages']['groups'] = isset($extraPackages['groups'][$row['id']]) ? $extraPackages['groups'][$row['id']] : [];
            $row['extraPackages']['additionalPackages'] = isset($extraPackages['additionalPackages'][$row['id']]) ? $extraPackages['additionalPackages'][$row['id']] : [];
            $data[] = $row;
        }

        return $data;
    }

    private function generateProductsHtml($pakagesProducts){
        $html = '';
        foreach ($pakagesProducts as $value) {
            $html .= $value['unit'] . ' x ' . $value['name'] . ' sold by ' . $value['supplier'].' <br>';
        }

        return $html;
    }

    /**
     * Get the data for virtual products to create or edit the template packages
     * @return array - returns an array with virtual products
     */
    public function getVirtualProductsByCategories(){
        global $database;
        $data = [];

        $sql = "SELECT
                    p.id AS idProduct,
                	p.virtualName AS productName,
                    pc.category
                FROM " . TABLES['products'] . " p
                INNER JOIN ".TABLES['product_categories']." pc
                    ON pc.id = p.idCategory
                ORDER BY pc.category, p.virtualName
        ";
        $result = $database->query($sql);

        while($row = $database->fetchArray($result)) {
            $data[$row['category']][] = $row;
        }

        return $data;
    }

    /**
     * get the supplier products by country grouped by category
     * @param  Int $idCountry the id of the country for which to get the products for
     * @return Array          the products from given country grouped by category
     */
    public function getProductsByCategory($idCountry) {
        global $database;
        $products = [];

        if($idCountry) {
            $sql = "
                    SELECT
                        p.idProduct AS idProduct,
                    	p.productName AS productName,
                        s.name AS supplierName,
                        p.unitCostPrice AS price,
                        p.unitVatCost AS vatPrice,
                        pc.category,
                        pc.hasMultipleRealProducts AS hasMultiple
                    FROM " . TABLES['suppliers_countries_products'] . " p
                    INNER JOIN " . TABLES['suppliers'] . " s
                        ON p.idSupplier = s.id
                    INNER JOIN " . TABLES['product_categories'] . " pc
                        ON pc.id = p.idProductCategory
                    WHERE p.idCountry = $idCountry AND p.isAvailable=1
                    ORDER BY productName, supplierName
            ";
            $result = $database->query($sql);

            while($row = $database->fetchArray($result)) {
                $products[$row['category']][] = $row;
            }
        }

        return $products;
    }

    /**
     * Get the data for products from database based on contry selected
     * @param  int $country which is the id of the country selected
     * @param  int $idPackage which is the id of the package selected
     * @return array - returns an array with raw information about products
     */
    private function getProducts($country, $idPackage = 0) {
        global $database;
        $data = [];
        $extraWhereClause = '';
        $joinClause = '';
        $quantitySelect = '';

        if($country != 0) {
            $extraWhereClause = " AND p.idCountry = ".$country;
        }

        if($idPackage != 0) {
            $quantitySelect = ", rpp.quantity AS quantity";
            $joinClause = "INNER JOIN " . TABLES['rel_package_products'] . " rpp
                                ON p.idProduct = rpp.idProduct
                            INNER JOIN
                                (SELECT
                                  rpp_last.idPackage,
                                  MAX(rpp_last.packageInstance) AS maxInstance
                                FROM
                                  " . TABLES['rel_package_products'] . " rpp_last
                                GROUP BY rpp_last.idPackage) last_instance
                                ON last_instance.idPackage = rpp.idPackage
                                AND last_instance.maxInstance = rpp.packageInstance";
            $extraWhereClause .= " AND rpp.idPackage = $idPackage";
        }

        $sql = "SELECT
                    p.idProduct AS idProduct,
                    p.isAvailable AS isAvailable,
                	p.productName AS productName,
                    s.name AS supplierName,
                    p.unitCostPrice AS price,
                    p.unitVatCost AS vatPrice,
                    pc.category
                    $quantitySelect
                FROM " . TABLES['suppliers_countries_products'] . " p
                INNER JOIN " . TABLES['suppliers'] . " s
                 ON p.idSupplier = s.id
                INNER JOIN " . TABLES['product_categories'] . " pc
                 ON pc.id = p.idProductCategory
                $joinClause
                $extraWhereClause";

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

        while($row = $database->fetchArray($result)) {
            $data[$row['category']][] = $row;
        }

        return $data;
    }

    /**
     * Verify package data from the UI
     * @param  array $packageData contains the products with corresponding info to be added in the package
     * @param  array $packageInfo contains the information about the package itself (name, description)
     * @param  bool  $isFromTemplate true or false if the package is created from template
     * @param  bool  $packageHasInstallation true or false if the package contains installation products
     * @param  bool  $isTemplate true or false if the package is template
     * @return array - returns a message or the formatted pakage data for adding the package in the DB
     */
    private function validatePackagesData($packageData, $packageInfo, $isFromTemplate, $packageHasInstallation, $isTemplate = 0) {
        global $database;
        $packageInfo->packageName = trim($packageInfo->packageName);

        if(!count($packageData, COUNT_RECURSIVE)){
            $data['messageData'][] = [
                    'code' => 'warning',
                    'message' => 'NO_PRODUCTS'
            ];

            return $data;
        }

        foreach($packageData as $category => $products) {
            if(!count($products)){
                $data['messageData'][] = [
                        'code' => 'warning',
                        'message' => 'NO_PRODUCTS'
                ];

                return $data;
            }
        }

        if(!$packageInfo->packageName) {
            $data['messageData'][] = [
                    'code' => 'error',
                    'message' => 'NO_INFO',
                    'field' => 'Package name'
            ];

            return $data;
        }

        $checkMsg = $database->invalidLength('Package name', $packageInfo->packageName, 150);
        if($checkMsg) {
            $data['messageData'][] = $checkMsg;

            return $data;
        }

        if(!$packageInfo->packageDescription) {
            $data['messageData'][] = [
                    'code' => 'error',
                    'message' => 'NO_INFO',
                    'field' => 'Package description'
            ];

            return $data;
        }


        if($isTemplate) {
            $packageNameExists = $this->checkIfTemplatePackageExists($packageInfo->packageName);
            if($packageNameExists) {
                $data['messageData'][] = [
                        'code' => 'error',
                        'message' => 'PACKAGE_TEMPLATE_NAME_EXISTS'
                ];

                return $data;
            }
        } else {
            if(!$packageInfo->idPackageType || !is_numeric($packageInfo->idPackageType)) {
                $data['messageData'][] = [
                        'code' => 'error',
                        'message' => 'INVALID_PACKAGE_TYPE',
                        'field' => 'Package type'
                ];

                return $data;
            }

            if($packageHasInstallation && !$packageInfo->additionalInstallationDays) {
                $data['messageData'][] = [
                        'code' => 'error',
                        'message' => 'ERROR_ADDITIONAL_INSTALLATION_DAYS',
                        'field' => 'Additional bussiness days until installation'
                ];

                return $data;
            }
            if($isFromTemplate) {
                if(!$packageInfo->idTemplate) {
                    $data['messageData'][] = [
                            'code' => 'error',
                            'message' => 'TEMPLATE_NOT_SET'
                    ];

                    return $data;
                }
            }
            if(!isset($packageInfo->packageReference) || !$packageInfo->packageReference){
                $data['messageData'][] = [
                        'code' => 'error',
                        'message' => 'NO_INFO',
                        'field' => 'Package reference'
                ];

                return $data;
            }
            if(strlen($packageInfo->packageReference) > 50){
                $data['messageData'][] = [
                        'code' => 'error',
                        'message' => 'STRING_TOO_LONG',
                        'field' => 'Package reference',
                        'limit' => 50
                ];

                return $data;
            }

            $idCountry = property_exists($packageInfo, 'idCountry') ? $packageInfo->idCountry : 0;
            if(isset($packageInfo->isForEdit) && $packageInfo->isForEdit) {
                if(!$packageInfo->idPackage) {
                    $data['messageData'][] = [
                            'code' => 'error',
                            'message' => 'PACKAGE_NOT_SELECTED'
                    ];

                    return $data;
                }

                if(count($packageData) == 0 || count($packageInfo) == 0) {
                    $data['messageData'][] = [
                            'code' => 'error',
                            'message' => 'NO_PRODUCTS'
                    ];

                    return $data;
                }

                $packageNameExists = $this->checkIfPackageExists($packageInfo->packageName, $idCountry, $packageInfo->idPackage);
            } else {
                $packageNameExists = $this->checkIfPackageExists($packageInfo->packageName, $idCountry);
            }

            if($packageNameExists) {
                $data['messageData'][] = [
                        'code' => 'error',
                        'message' => 'PACKAGE_NAME_EXISTS',
                        'packageName' => $packageInfo->packageName
                ];

                return $data;
            }
        }
        return [];
    }

    /**
     * Insert package with selected products in DB
     * @param  array $packageData contains the products with corresponding info to be added in the package
     * @param  array $packageInfo contains the information about the package itself (name, description)
     * @return array - returns a failure or success message for adding the package in the DB along with info needed
     */
    public function createPackagesData($packageData, $packageInfo) {
        global $database;
        $messageData = [];
        $packagesData = [];
        $productsArray = ['idProduct', 'productUnit', 'category'];
        $packageData = json_decode($packageData);
        $packageInfo = json_decode($packageInfo);
        $packageInfo->packageName = trim($packageInfo->packageName);
        $packageHasInstallation = false;

        if(empty($packageData)){
            $messageData['messageData'][] = [
                    'code' => 'error',
                    'message' => 'PACKAGE_DATA_EMPTY'
                ];
            return $messageData;
        }

        if(count((array) $packageData) !== count(array_unique((array) $packageData, SORT_REGULAR))){
            $messageData['messageData'][] = [
                    'code' => 'error',
                    'message' => 'DUPLICATE_PRODUCTS'
                ];
            return $messageData;
        }

        foreach ($packageData as $key => $value) {
            if(!property_exists($value, 'productUnit') || $value->productUnit > 100 || $value->productUnit < 0) {
                $messageData['messageData'][] = [
                        'code' => 'error',
                        'message' => 'ONLY_NUMBERS'
                    ];
                break;
            }
            foreach($productsArray as $field) {
                $packagesData[$key][$field] = $database->escapeValue($value->$field);
            }
            if($value->category === 'installation') {
                $packageHasInstallation = true;
            }
        }

        foreach ($packageInfo as $key => $value) {
            $packageInfo->$key = $database->escapeValue($value);
        }

        $isFromTemplate = false;
        $messageData = $this->validatePackagesData($packageData, $packageInfo, $isFromTemplate, $packageHasInstallation);

        if($messageData) {
            return $messageData;
        }

        $packageId = $this->insertPackagesInDb($packageInfo);
        $insertedRows = $this->insertPackageItemsInDb($packagesData, $packageId);
        $data['messageData'][] = [
            'code' => 'success',
            'message' => 'PACKAGE_ADDED'
        ];
        $data['packageName'] = $packageInfo->packageName;
        $data['productsNumber'] = $insertedRows;

        return $data;
    }

    /**
     * Insert template package with selected virtual products in DB
     * @param  array $packageData contains the products with corresponding info to be added in the template package
     * @param  array $packageInfo contains the information about the package itself (name, description)
     * @return array - returns a failure or success message for adding the package in the DB along with info needed
     */
    public function createTemplatePackagesData($packageInfo, $products) {
        global $database;
        $packageInfo = json_decode($packageInfo);
        $products = json_decode($products);
        $messageData = [];
        $productsArray = ['idProduct', 'productUnit'];
        $packageHasInstallation = false;

        foreach ($products as $key => $value) {
            if(gettype($value) === 'object') {
                if(!property_exists($value, 'productUnit') || $value->productUnit > 100 || $value->productUnit < 0) {
                    $messageData['messageData'] = [
                            'code' => 'error',
                            'message' => 'ONLY_NUMBERS'
                        ];
                    break;
                }
                foreach($productsArray as $field) {
                    $products[$key]->$field = $database->escapeValue($value->$field);
                }
                if($value->category === 'installation') {
                    $packageHasInstallation = true;
                }
            }
        }

        foreach ($packageInfo as $key => $value) {
            $packageInfo->$key = $database->escapeValue($value);
        }
        $messageData = $this->validatePackagesData($products, $packageInfo, false, $packageHasInstallation, 1);

        if($messageData)
            return $messageData;

        $insertedTemplatePackageInfoRows = $this->insertTemplatePackageInfoInDb($packageInfo);
        $insertedTemplatePackageDataRows = $this->insertTemplatePackageDataInDb($products, $insertedTemplatePackageInfoRows);

        if($insertedTemplatePackageDataRows < 1) {
            $data['messageData'][] = [
                'code' => 'warning',
                'message' => 'TEMPLATE_PACKAGE_NOT_ADDED'
            ];
        } else {
            $data['messageData'][] = [
                'code' => 'success',
                'message' => 'TEMPLATE_PACKAGE_ADDED'
            ];
        }

        $data['packageName'] = $packageInfo->packageName;
        $data['productsNumber'] = $insertedTemplatePackageDataRows;

        return $data;
    }

    /**
     * Check if package name already exists in the DB
     * @param  array $packageInfo - the name and description of the template package wanted to be added
     * @return int - will return the id of the template package name inserted
     */
    private function insertTemplatePackageInfoInDb($packageInfo) {
        global $database;

        $sql = "INSERT INTO ".TABLES['packages_templates']."
                    (templateName, templateDescription)
                VALUES (
                    '".$packageInfo->packageName."',
                    '".$packageInfo->packageDescription."'
                    )
        ";
        $result = $database->query($sql);
        return $database->getInsertId();
    }

    /**
     * Check if template package name already exists in the DB
     * @param  string $packageName - the name of the package wanted to be added
     * @return bool - true if the package already exists, false otherwise
     */
    private function checkIfTemplatePackageExists($packageName) {
        global $database;

        $sql = "SELECT
                    p.templateName
                FROM
                    ".TABLES['packages_templates']." p
                WHERE
                    p.templateName = '".$packageName."'
                LIMIT 1
        ";
        $result = $database->query($sql);
        $numRows = $database->numRows($result);

        return $numRows > 0;
    }

    /**
     * Check if package name already exists in the DB
     * @param  string $packageName - the name of the package wanted to be added
     * @param  int $idCountry - the id of the country where the package will be added
     * @param  int $idPackage - the id of the package - check if there is another package with the same name
     * @return bool - true if the package already exists, false otherwise
     */
    private function checkIfPackageExists($packageName, $idCountry, $idPackage = 0) {
        global $database;
        $whereClause = '';

        if($idCountry) {
            $whereClause = " AND p.idCountry = $idCountry";
        }

        if($idPackage) {
            $whereClause .= " AND p.id <> $idPackage";
        }

        $sql = "SELECT
                    p.name
                FROM
                    ".TABLES['packages']." p
                WHERE
                    p.name = '".$packageName."'
                    $whereClause
        ";
        $result = $database->query($sql);

        return $database->numRows($result) > 0;
    }

    /**
     * Insert package information in the DB
     * @param  array $packageInfo contains the information about the package itself (name, description)
     * @return int - returns the id of the last inserted package in the DB
     */
    private function insertPackagesInDb($packageInfo) {
        global $database;
        $idTemplate = property_exists($packageInfo, 'idTemplate') ? $packageInfo->idTemplate : 'null';

        $sql = "INSERT INTO ".TABLES['packages']."
                (idCountry, idTemplate, reference, name, description, additionalInstallDays, idPackageType)
                VALUES (
                    ".$packageInfo->idCountry.",
                    ".$idTemplate.",
                    '".$packageInfo->packageReference."',
                    '".$packageInfo->packageName."',
                    '".$packageInfo->packageDescription."',
                    '".$packageInfo->additionalInstallationDays."',
                    '".$packageInfo->idPackageType."'
                    )";
        $database->query($sql);

        return $database->getInsertId();
    }

    /**
     * Insert package data (products whithin the package) in the DB
     * @param  array $data contains the products ids and units included in the package
     * @return int - returns number of products added in the package
     */
    private function insertPackageItemsInDb($data, $packageId) {
        global $database;

        $sql = "INSERT INTO ".TABLES['rel_package_products']."
                    (idPackage, idProduct, quantity)
                VALUES ";
        foreach($data as $value) {
            $sql .= "(
                    ".$packageId.",
                    ".$value['idProduct'].",
                    ".$value['productUnit']."
                    ),";
        }

        $sql = rtrim($sql, ",");
        $database->query($sql);

        return $database->affectedRows();
    }

    /**
     * Insert package data (products whithin the package) in the DB
     * @param  array $data contains the products ids and units included in the package
     * @return int - returns number of products added in the package
     */
    private function insertTemplatePackageDataInDb($data, $packageId) {
        global $database;

        $sql = "INSERT INTO ".TABLES['rel_package_product_template']."
                    (idPackageTemplate, idVirtualProduct, quantity)
                VALUES ";
        foreach($data as $value) {
            $sql .= "(
                    ".$packageId.",
                    ".$value->idProduct.",
                    ".$value->productUnit."
                    ),";
        }
        $sql = rtrim($sql, ",");
        $database->query($sql);

        return $database->affectedRows();
    }

    /**
     * get the list of packages templates and their info
     * @return Array of template packages
     */
    public function getTemplatePackages() {
        global $database;
        $templateData = [];
        $idTemplate = 0;

        $sql = "SELECT
                	pt.id as idTemplate,
                    p.id as idVirtualProduct,
                    pt.templateName AS name,
                    pt.templateDescription AS description,
                    p.virtualName,
                    rppt.quantity,
                    pc.category
                FROM ".TABLES['packages_templates']." pt
                INNER JOIN ".TABLES['rel_package_product_template']." rppt
                    ON rppt.idPackageTemplate = pt.id
                INNER JOIN ".TABLES['products']." p
                    ON rppt.idVirtualProduct = p.id
                INNER JOIN ".TABLES['product_categories']." pc
                    ON pc.id = p.idCategory
                ORDER BY templateName, virtualName
        ";

        $query = $database->query($sql);
        while($row = $database->fetchArray($query)) {
            if($idTemplate != $row['idTemplate']) {
                $templateData[] = [
                    'idTemplate' => $row['idTemplate'],
                    'templateName'=> $row['name'],
                    'templateDescription'=> $row['description']
                ];
            }
            $productData[$row['idTemplate']][$row['category']][] = [
                'idProduct'=> $row['idVirtualProduct'],
                'virtualName'=> $row['virtualName'],
                'quantity'=> $row['quantity']
            ];
            $idTemplate = $row['idTemplate'];
        }

        foreach($templateData as $key => $values) {
            $templateData[$key]['products'] = $productData[$values['idTemplate']];
        }

        return $templateData;
    }

    /**
     * get the list of packages templates for editing
     * @return Array of template packages
     */
    public function getTemplatePackagesForEdit() {
        global $database;
        $templateDataForEdit = [];

        $sql = "SELECT
                	pt.id as idTemplate,
                    p.id as idVirtualProduct,
                    pt.templateName AS name,
                    pt.templateDescription AS description,
                    p.virtualName,
                    rppt.quantity,
                    pc.category
                FROM ".TABLES['packages_templates']." pt
                INNER JOIN ".TABLES['rel_package_product_template']." rppt
                    ON rppt.idPackageTemplate = pt.id
                INNER JOIN ".TABLES['products']." p
                    ON rppt.idVirtualProduct = p.id
                INNER JOIN ".TABLES['product_categories']." pc
                    ON pc.id = p.idCategory
                ORDER BY name, virtualName
        ";

        $query = $database->query($sql);
        while($row = $database->fetchArray($query)) {
            $templateData[$row['idTemplate']] = [
                'id' => $row['idTemplate'],
                'name'=> $row['name'],
                'description'=> $row['description']
            ];
            $productData[$row['idTemplate']][] = [
                'idProduct'=> $row['idVirtualProduct'],
                'productName'=> $row['virtualName'],
                'productUnit'=> $row['quantity'],
                'category'=> $row['category']
            ];
        }
        foreach($templateData as $id => $values) {
            $templateData[$id]['products'] = $productData[$id];
            array_push($templateDataForEdit, $templateData[$id]);
        }

        return $templateDataForEdit;
    }

    /**
     * get customers list linked to the commercial lead
     * @param  INT $idPackage id for package. 0 if no package is desired
     * @return Array    list of customers linked to a commercial lead
     */
    public function getComercialLeadCustomers($idPackage){
        global $database, $user;
        $idUser = $user->getUserId();

        $sql = "SELECT
                    c.id,
                    c.name AS customer,
                    IFNULL(plcl.idCustomer, 0) AS selectedCustomer,
                    IF(u.idCompany = clCompany.idCompany, 1, 0) AS isSameCompanyAsCl
                FROM
                    ".TABLES['customers']." c
                INNER JOIN ".TABLES['rel_commercial_lead_customers']." rclc
                  ON rclc.idCustomer=c.id AND isLinkEnabled=1
                INNER JOIN ".TABLES['commercial_leads']." cl
                  ON cl.id=rclc.idCommercialLead
                LEFT OUTER JOIN
                  (SELECT DISTINCT idCustomer, idCommercialLead
                      FROM ".TABLES['price_list_commercial_lead']."
                      WHERE idPackage=$idPackage) plcl
                  ON plcl.idCommercialLead=cl.id AND plcl.idCustomer=c.id
                INNER JOIN users u
                  ON u.id = c.idUser
                INNER JOIN
                  (SELECT
                    us.idCompany,
                    us.id
                  FROM
                    users us
                  WHERE us.id = $idUser) clCompany
                  ON clCompany.id = cl.idUser
                WHERE cl.idUser=$idUser
                ORDER BY customer";

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

    /**
     * get package details
     * @param  INT $idPacakge id for package
     * @return HashArray            details for a package
     */
    public function getPackageInfo($idPacakge){
        global $database, $user;

        $idPacakge = $database->escapeValue($idPacakge);
        $priceSql = "";
        if($user->getUserType() === USER_TYPES['BROKER']){
            $priceSql .= ", prod.unitCostPrice, prod.isPriceRecurring, prod.payPeriod";
        }

        $sql = "SELECT
                    p.id,
                    p.name,
                    p.description,
                    c.name as country
                FROM
                    ".TABLES['packages']." p
                INNER JOIN ".TABLES['countries']." c
                    ON c.id=p.idCountry
                WHERE p.id=$idPacakge";
        $data['info'] = $database->fetchResultArray($sql);

        $sql = "SELECT rpp.quantity,
                prod.productName,
                pt.type AS productType
                $priceSql
                    FROM ".TABLES['rel_package_products']." rpp
                INNER JOIN ".TABLES['suppliers_countries_products']." prod
                    ON prod.idProduct=rpp.idProduct
                INNER JOIN ".TABLES['product_categories']." pc
                    ON pc.id=prod.idProductCategory
                INNER JOIN ".TABLES['product_types']." pt
                    ON pt.id = pc.idType
                INNER JOIN
                    (SELECT
                      rpp_last.idPackage,
                      MAX(rpp_last.packageInstance) AS maxInstance
                    FROM
                      ".TABLES['rel_package_products']." rpp_last
                    GROUP BY rpp_last.idPackage) last_instance
                    ON last_instance.idPackage = rpp.idPackage
                    AND last_instance.maxInstance = rpp.packageInstance
                WHERE rpp.idPackage=$idPacakge
                ORDER BY pt.type, prod.productName
                 ";
        $query = $database->query($sql);
        $data['products'] = [];
        while($row = $database->fetchArray($query)){
            $data['products'][ucfirst($row['productType'])][] = $row;
        }

        return $data;
    }

    /**
     * get list of prices for a package (broker prices and commercial lead prices)
     * @param  INT $idPackage id for package
     * @return HashArray   brokerPrices and  commercialLeadPrices lists objet
     */
    public function getBrokerPriceList($idPackage, $idCustomer = 0){
        global $database, $user;

        $data = [];
        $idPackage = $database->escapeValue($idPackage);
        $prices = new Prices();
        $interestRate = new InterestRate();

        if($idCustomer) {
            $interestRateValue = $interestRate->getInterestRateForCustomers($idCustomer);
        } else {
            $interestRateValue = $interestRate->getInterestRate()['interestRate'];
        }

        $data['interestRate'] = $interestRateValue;

        $brokerPrices = $prices->getPriceTypes($idPackage, true, 'category', $interestRateValue);
        $clDefaultPrices = $prices->getClDefaultPrices($idPackage);
        $commissionSplit = $brokerPrices['commissionSplit'];
        $totalCost = $prices->calculatePackageTotalCost($brokerPrices['productsPrices']);

        foreach ($brokerPrices['priceTypes'] as &$paymentType) {
            if(isset($clDefaultPrices[$paymentType['idPayType']])){
                $paymentType['defaultExtra'] = $clDefaultPrices[$paymentType['idPayType']]['fixedExtra'];
                $paymentType['defaultRecurent'] = $clDefaultPrices[$paymentType['idPayType']]['recurentExtra'];
                $paymentType['defaultServicesRecurent'] = $clDefaultPrices[$paymentType['idPayType']]['servicesExtra'];

            }else{
                $paymentType['defaultExtra'] = 0;
                $paymentType['defaultRecurent'] = 0;
                $paymentType['defaultServicesRecurent'] = 0;
            }

            $paymentType['margins'] = $prices->getPriceMargins($commissionSplit ,$paymentType, $totalCost);
        }


        $data['brokerPrices'][$idCustomer] = $brokerPrices['priceTypes'];
        $clPrices = $prices->getCommercialLeadPrices($idPackage, $user->getUserId());
        $data['commercialLeadPrices'] = isset($clPrices[$idPackage]) ? $clPrices[$idPackage] : [];
        $data['idCustomer'] = $idCustomer;

        return $data;
    }

    /**
     * get the prices for a specific customer and package
     * @param  Int $idPackage  id of the package
     * @param  Int $idCustomer id of the customer
     * @return HashArray       customer pricelist
     */
    public function getCustomerPrices($idPackage, $idCustomer) {
        return $this->getBrokerPriceList($idPackage, $idCustomer);
    }

    /**
     * Validate values for adding/updateing prices
     * @param  INT $idCommercialLead id ofr commercial lead
     * @param  INT $idCustomer       id for customer
     * @param  INT $idPacakge        id for package
     * @param  INT $pay_key          id for payment type
     * @param  Object $price            price object containg new values (fixedCommission, recurentCommission, visibleToCustomer)
     * @return Array                  error message array or empty array if no error
     */
    public function validatePriceValues($idCommercialLead, $idCustomer, $idPacakge, $pay_key, $price){
        global $database;

        $data =[];
        $max_value = 1 * pow(10, 13);

        $checkMessage = $database->isEmpty('idCommercialLead', $idCommercialLead);
        if($checkMessage){
            $data['messages'][] = $checkMessage;
        }

        $checkMessage = $database->isEmpty('idCustomer', $idCustomer);
        if($checkMessage){
            $data['messages'][] = $checkMessage;
        }

        $checkMessage = $database->isEmpty('idPackage', $idPacakge);
        if($checkMessage){
            $data['messages'][] = $checkMessage;
        }

        $checkMessage = $database->invalidNumber('fixedCommission', $price->fixedCommission, 0, $max_value);
        if($checkMessage){
            $data['messages'][] = $checkMessage;
        }

        $checkMessage = $database->invalidNumber('recurentCommission', $price->recurentCommission, 0, $max_value);
        if($checkMessage){
            $data['messages'][] = $checkMessage;
        }

        $checkMessage = $database->invalidNumber('recurentServicesCommission', $price->recurentServicesCommission, 0, $max_value);
        if($checkMessage){
            $data['messages'][] = $checkMessage;
        }

        if($price->visibleToCustomer != 0 && $price->visibleToCustomer != 1){
            $data['messages'][] = [
                    'code' => 'error',
                    'message' => 'INVALID_VISIBLE_TO_CUSTOMER',
                    'key' => 'visibleToCustomer'
            ];
        }

        return $data;
    }

    /**
     * get commercial lead id for the logged in user
     * @return INT id for commercial lead
     */
    private function getCommercialLead(){
        global $database, $user;

        $sql = "SELECT id as idCommercialLead
                FROM ".TABLES['commercial_leads']." cl
                WHERE idUser=" . $user->getUserId();
        $query = $database->query($sql);
        $commercialLead = $database->fetchArray($query);

        return $commercialLead['idCommercialLead'];
    }

    private function validateDefaultPriceValues($idCommercialLead, $idPackage, $price){
        global $database;

        $data =[];
        $max_value = 1 * pow(10, 13);

        $checkMessage = $database->isEmpty('idCommercialLead', $idCommercialLead);
        if($checkMessage){
            $data['messages'][] = $checkMessage;
        }

        $checkMessage = $database->isEmpty('idPackage', $idPackage);
        if($checkMessage){
            $data['messages'][] = $checkMessage;
        }

        $checkMessage = $database->isEmpty('idPayType', $price->idPayType);
        if($checkMessage){
            $data['messages'][] = $checkMessage;
        }

        $checkMessage = $database->invalidNumber('defaultExtra', $price->defaultExtra, 0, $max_value);
        if($checkMessage){
            $data['messages'][] = $checkMessage;
        }

        $checkMessage = $database->invalidNumber('defaultRecurent', $price->defaultRecurent, 0, $max_value);
        if($checkMessage){
            $data['messages'][] = $checkMessage;
        }

        $checkMessage = $database->invalidNumber('defaultServicesRecurent', $price->defaultServicesRecurent, 0, $max_value);
        if($checkMessage){
            $data['messages'][] = $checkMessage;
        }

        return $data;
    }

    public function isDefaultPriceSet($idCommercialLead, $idPackage, $idPayType){
        global $database;

        $sql = "SELECT idCustomer
                FROM ".TABLES['price_list_commercial_lead']."
                WHERE idCommercialLead=$idCommercialLead
                    AND idPackage=$idPackage
                    AND idPaymentType=$idPayType
                    AND idCustomer IS NULL
                LIMIT 1";
        $query = $database->query($sql);

        return $database->numRows($query) > 0;
    }

    public function updateDefaultPrices($idPackage, $defaultPrices){
        global $database;

        $idPackage = $database->escapeValue($idPackage);
        $defaultPrices = json_decode($defaultPrices);
        $idCommercialLead = $this->getCommercialLead();

        if(count($defaultPrices) <= 0){
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'PLEASE_SET_DEFAULT_PRICES'
            ];

            return $data;
        }

        $valuesSqlIns = "";
        $affectedRows = 0;
        $data = [];
        foreach ($defaultPrices as $price) {
            $data = array_merge($data, $this->validateDefaultPriceValues($idCommercialLead, $idPackage, $price));
            if(!empty($data)){
                return $data;
            }
            $price->visibleToCustomer = $price->visibleToCustomer ? 1 : 0;
            if($this->isDefaultPriceSet($idCommercialLead, $idPackage, $price->idPayType)){
                $sqlUpd = "UPDATE ".TABLES['price_list_commercial_lead']."
                        SET fixedExtra=".$database->escapeValue($price->defaultExtra).",
                        recurentExtra=".$database->escapeValue($price->defaultRecurent).",
                        servicesExtra=".$database->escapeValue($price->defaultServicesRecurent).",
                        visibleToCustomer=".$database->escapeValue($price->visibleToCustomer)."
                        WHERE idCommercialLead=".$database->escapeValue($idCommercialLead)."
                            AND idPackage=".$database->escapeValue($idPackage)."
                            AND idPaymentType=".$database->escapeValue($price->idPayType)."
                            AND idCustomer IS NULL";
                $query = $database->query($sqlUpd);
                $affectedRows += $database->affectedRows();
            }else{
                $valuesSqlIns .= "(".$database->escapeValue($idCommercialLead).",
                                ".$database->escapeValue($idPackage).",
                                ".$database->escapeValue($price->idPayType).",
                                ".$database->escapeValue($price->defaultExtra).",
                                ".$database->escapeValue($price->defaultRecurent).",
                                ".$database->escapeValue($price->defaultServicesRecurent).",
                                ".$database->escapeValue($price->visibleToCustomer)."
                            ),";
            }
        }

        if(!empty($data)){
            return $data;
        }

        $valuesSqlIns = rtrim($valuesSqlIns, ',');

        if($valuesSqlIns){
            $sqlIns = "INSERT INTO ".TABLES['price_list_commercial_lead']."
                    (idCommercialLead, idPackage, idPaymentType, fixedExtra, recurentExtra, servicesExtra, visibleToCustomer)
                    VALUES $valuesSqlIns";
            $query = $database->query($sqlIns);

            $affectedRows += $database->affectedRows();
        }

        if($affectedRows <1){
            $data['messages'][] = [
                'code' => 'warning',
                'message' => 'NO_CHANGES_DEFAULT'
            ];

            return $data;
        }

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

        return $data;
    }

    /**
     * add or update prices for pacakges to sell by commercial lead
     * @param  INT $idPackage         id for package
     * @param  Array $selectedCustomers prices object grouped by customers
     * @return Array                   add update message array
     */
    public function updateMyPackage($idPackage, $selectedCustomers){
        global $database;

        $idPackage = $database->escapeValue($idPackage);
        $selectedCustomers = json_decode($selectedCustomers);
        $idCommercialLead = $this->getCommercialLead();

        if(count($selectedCustomers) <= 0){
            $data['messages'][] = [
                'code' => 'warning',
                'message' => 'NO_CHANGES'
            ];

            return $data;
        }

        $valuesSql = "";
        $data = [];
        foreach ($selectedCustomers as $customer) {
            foreach ($customer->prices as $pay_key => $price) {
                $price->visibleToCustomer = $price->visibleToCustomer ? 1 : 0;
                $data = array_merge($data, $this->validatePriceValues($idCommercialLead, $customer->id, $idPackage, $pay_key, $price));
                $valuesSql .= "(".$database->escapeValue($idCommercialLead).",
                                ".$database->escapeValue($customer->id).",
                                ".$database->escapeValue($idPackage).",
                                ".$database->escapeValue($pay_key).",
                                ".$database->escapeValue($price->fixedCommission).",
                                ".$database->escapeValue($price->recurentCommission).",
                                ".$database->escapeValue($price->recurentServicesCommission).",
                                ".$database->escapeValue($price->visibleToCustomer)."
                            ),";
            }
        }

        if(!empty($data)){
            return $data;
        }

        $valuesSql = rtrim($valuesSql, ',');

        $sql = "INSERT INTO ".TABLES['price_list_commercial_lead']."
                (idCommercialLead, idCustomer, idPackage, idPaymentType, fixedExtra, recurentExtra, servicesExtra, visibleToCustomer)
                VALUES $valuesSql
                ON DUPLICATE KEY UPDATE
                    fixedExtra=VALUES(fixedExtra),
                    recurentExtra=VALUES(recurentExtra),
                    servicesExtra=VALUES(servicesExtra),
                    visibleToCustomer= VALUES(visibleToCustomer)";

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

        if($database->affectedRows() <1){
            $data['messages'][] = [
                'code' => 'warning',
                'message' => 'NO_CHANGES'
            ];

            return $data;
        }

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

        return $data;
    }

    /**
     * get product prices list and comissions
     * @param  INT $idPackage id of the package
     * @return json   list of prices and list of comission
     */
    public function getPriceTypes($idPackage, $sumBy){
        $prices = new Prices();

        return $prices->getPriceTypes($idPackage, false, $sumBy);
    }

    /**
     * update values for broker prices and comissions
     * @param  INT $idPackage                 id for package
     * @param  Objects Array $pricesValues          Array with prices to be inserted/updated
     * @param  Objects Array $commissionSplit Array with commission to be inserted/updated
     * @return Array                          Update message
     */
    public function updateBrokerPricesAndCommission($idPackage, $pricesValues, $commissionSplit){
        $prices = new Prices();

        return $prices->updateBrokerPricesAndCommission($idPackage, $pricesValues, $commissionSplit);
    }

    /**
     * Get the products for the selected package for edit
     * @param  int $idPackage which is the id of the package selected
     * @return array - returns an array with products and data to be displayed and an empty package array
     */
    public function getProductsInPackage($idCountry, $idPackage = 0) {
        if(!$idPackage) {
            return ['messageData' => [
                'code' => 'error',
                'message' => 'PACKAGE_NOT_SELECTED'
                ]
            ];
        }

        $allPackagesProducts = $this->getProducts($idCountry, $idPackage);
        if(!$allPackagesProducts) {
            return ['messageData' => [
                'code' => 'error',
                'message' => 'NO_PRODUCTS'
                ]
            ];
        }

        return $allPackagesProducts;
    }

    /**
     * Update product info for given package
     * @param  array $packageData the products and info about them from the package
     * @param  array $packageInfo which contains the name, despription and product informations for package to be edited
     * @return array - returns an array with status message
     */
    public function updateInformationDataInPackage($packageData, $packageInfo) {
        global $database;
        $noOfRowsEdited = 0;
        $suppliersProducts = new SuppliersProducts();
        $packageHasInstallation = false;

        $packageData = json_decode($packageData);
        $packageInfo = json_decode($packageInfo);

        if(empty($packageData)){
            $messageData['messageData'][] = [
                'code' => 'error',
                'message' => 'PACKAGE_DATA_EMPTY'
            ];
            return $messageData;
        }

        foreach ($packageData as $key => $product) {
            if(!property_exists($product, 'productUnit') || $product->productUnit > 100 || $product->productUnit < 0) {
                $messageData['messageData'][] = [
                    'code' => 'error',
                    'message' => 'ONLY_NUMBERS'
                ];
                break;
            }

            if($product->category === 'installation') {
                $packageHasInstallation = true;
            }
        }

        foreach ($packageInfo as $key => $value) {
            $packageInfo->$key = $database->escapeValue(trim($value));
        }

        $isFromTemplate = false;
        $messageData = $this->validatePackagesData($packageData, $packageInfo, $isFromTemplate, $packageHasInstallation);

        if($messageData) {
            return $messageData;
        }

        $idPackage = $packageInfo->idPackage;
        $data['packageUpdated'] = $packageInfo;

        $extraWhereClause = " AND idCountry = ".$packageInfo->idCountry;

        $sqlPackageName = "UPDATE " . TABLES['packages'] ."
                                SET name = '".$packageInfo->packageName."',
                                    reference = '".$packageInfo->packageReference."',
                                    description = '".$packageInfo->packageDescription."',
                                    additionalInstallDays = ".$packageInfo->additionalInstallationDays.",
                                    idPackageType = ".$packageInfo->idPackageType."
                            WHERE id = $idPackage
                            AND idCountry = ".$packageInfo->idCountry;

        $result = $database->query($sqlPackageName);
        $noOfRowsEdited += $database->affectedRows();

        $sqlSelect = "
                        SELECT MAX(rpp.packageInstance)+1 AS maxPackageInstance
                            FROM ".TABLES['rel_package_products']." rpp
                        WHERE rpp.idPackage = $idPackage
                        LIMIT 1
        ";
        $instance = $database->fetchResultArray($sqlSelect);
        $packageInstance = $instance[0]['maxPackageInstance'];

        $sqlPackageProductsInsert = "
                                    INSERT IGNORE INTO ".TABLES['rel_package_products']."
                                        (packageInstance, idPackage, idProduct, quantity)
                                    VALUES
        ";
        foreach($packageData as $product) {
            $sqlPackageProductsInsert .= "(". $packageInstance .", "
                                            . $idPackage . ", "
                                            . $product->idProduct . ", "
                                            . $product->productUnit . "),";
        }

        $sqlPackageProductsInsert = rtrim($sqlPackageProductsInsert, ',');
        $result = $database->query($sqlPackageProductsInsert);
        $noOfRowsEdited += $database->affectedRows();

        if($noOfRowsEdited > 0) {
            $updatedStatus = $suppliersProducts->updatePackageAvailability(0, $idPackage);
            if($updatedStatus > 0){
                $data['messageData'][] = [
                    'code' => 'success',
                    'message' => 'PACKAGES_STATUS_UPDATED'
                ];
            }

            $data['messageData'][] = [
                'code' => 'success',
                'message' => 'PACKAGE_UPDATED'
            ];
        } else {
            $data['messageData'][] = [
                'code' => 'warning',
                'message' => 'PACKAGE_PRODUCTS_UNCHANGED'
            ];
        }

        return $data;
    }

    /**
     * Update product info for given package template
     * @param  int $idPackage which is the id of the package selected
     * @param  array $productsInfo which contains the name, despription and product informations for package to be edited
     * @return array - returns an array with status message
     */
    public function updateInformationDataInPackageTemplate($packageInfo, $products) {
        global $database;
        $idsProducts = '';
        $noOfRowsEdited = 0;
        $packageInfo = json_decode($packageInfo);
        $products = json_decode($products);
        $data['packageUpdated'] = $packageInfo;

        $data['messageData'] = $this->validateEditPackageTemplateData($packageInfo, $products);

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

        foreach ($packageInfo as $packKey => $packValue) {
            $packageInfo->$packKey = $database->escapeValue(trim($packValue));
        }

        foreach ($products as $category => $prodValue) {
            foreach ($prodValue as $prodKey => $productDetail) {
                $products[$category]->$prodKey = $database->escapeValue(trim($productDetail));
            }
        }

        $sqlPackageName = "UPDATE " . TABLES['packages_templates'] ."
                                SET templateName = '".$packageInfo->packageName."',
                                    templateDescription = '".$packageInfo->packageDescription."'
                            WHERE id = ".$packageInfo->idPackage."
        ";

        $result = $database->query($sqlPackageName);
        if($database->affectedRows() >= 0) {
            $noOfRowsEdited += $database->affectedRows();
        }

        $sqlPackageProductsInsert = "INSERT INTO " . TABLES['rel_package_product_template'] . "
                                        (idPackageTemplate, idVirtualProduct, quantity)
                                    VALUES
        ";

        foreach($products as $product) {
            $sqlPackageProductsInsert .= "(". $packageInfo->idPackage.", "
                                            . $product->idProduct . ", "
                                            . $product->productUnit . "),";
            $idsProducts .= $database->escapeValue($product->idProduct).",";
        }
        $idsProducts = rtrim($idsProducts, ',');
        $sqlPackageProductsInsert = rtrim($sqlPackageProductsInsert, ',');
        $sqlPackageProductsInsert .= " ON DUPLICATE KEY UPDATE
                                        quantity=VALUES(quantity)";

        $result = $database->query($sqlPackageProductsInsert);
        if($database->affectedRows() >= 0) {
            $noOfRowsEdited += $database->affectedRows();
        }

        $sqlDeleteOldProducts = "DELETE FROM " . TABLES['rel_package_product_template'] . "
                                WHERE idPackageTemplate = ".$packageInfo->idPackage."
                                AND idVirtualProduct NOT IN ($idsProducts)
        ";
        $result = $database->query($sqlDeleteOldProducts);
        if($database->affectedRows() >= 0) {
            $noOfRowsEdited += $database->affectedRows();
        }

        if($noOfRowsEdited > 0) {
            $data['messageData'][] = [
                'code' => 'success',
                'message' => 'PACKAGE_UPDATED'
            ];
        } else {
            $data['messageData'][] = [
                'code' => 'warning',
                'message' => 'PACKAGE_PRODUCTS_UNCHANGED'
            ];
        }

        return $data;
    }

    /**
     * Validate data for package template to edit
     * @param  int $idPackage which is the id of the package selected
     * @param  array $productsInfo which contains the name, despription and product informations for package to be edited
     * @return array - returns an array with status message
     */
    private function validateEditPackageTemplateData($packageInfo, $productsInfo) {
        global $database;
        $data = [];

        if(!count($productsInfo, COUNT_RECURSIVE)){
            $data['messageData'][] = [
                    'code' => 'warning',
                    'message' => 'NO_PRODUCTS'
            ];

            return $data;
        }

        foreach($productsInfo as $category => $products) {
            if(!count($products)){
                $data['messageData'][] = [
                        'code' => 'warning',
                        'message' => 'NO_PRODUCTS'
                ];

                return $data;
            }
        }

        if(!$packageInfo->idPackage) {
            $data[] = [
                'code' => 'error',
                'message' => 'PACKAGE_NOT_SELECTED'
            ];

            return $data;
        }

        if(!$packageInfo->packageName || !$packageInfo->packageDescription) {
            $data[] = [
                'code' => 'error',
                'message' => 'PACKAGE_NAME_DESCRIPTION_MISSING'
            ];

            return $data;
        }

        $checkMsg = $database->invalidLength('Package name', $packageInfo->packageName, 150);
        if($checkMsg) {
            $data[] = $checkMsg;

            return $data;
        }

        $sql = "SELECT templateName
                    FROM ".TABLES['packages_templates']." pt
                WHERE pt.templateName = '".$database->escapeValue($packageInfo->packageName)."'
                    AND pt.id <> ".$database->escapeValue($packageInfo->idPackage)."
        ";

        $result = $database->query($sql);
        if($database->numRows($result) > 0) {
            $data[] = [
                'code' => 'error',
                'message' => 'PACKAGE_NAME_EXISTS'
            ];
            return $data;
        }

        return $data;
    }

    public function getProductCategories() {
        return $this->suppliersProducts->getProductCategories();
    }

    /**
     * inserts the virtual product
     * @param String $productName the name of the new virtual product
     * @param Int $idCategory     the category id of the virtual product
     * @return  Array             confirmation message
     */
    public function addVirtualProduct($productName, $idCategory) {
        global $database;

        $productName = $database->escapeValue($productName);
        $idCategory = $database->escapeValue($idCategory);
        $data = $this->validateVirtualProductData($productName, $idCategory);
        if(!empty($data)) {
            return $data;
        }

        $sql = "
            INSERT INTO ".TABLES['products']."
                (virtualName, idCategory)
            VALUES (
                '$productName',
                $idCategory
                )
        ";
        $query = $database->query($sql);

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

        return $data;
    }

    private function validateVirtualProductData($productName, $idCategory) {
        global $database;
        $data = [];

        if($productName === '') {
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'NO_VIRTUAL_PRODUCT_NAME'
            ];

            return $data;
        }

        if($idCategory == 0) {
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'NO_VIRTUAL_PRODUCT_CATEGORY'
            ];

            return $data;
        }

        if($database->invalidLength('Virtual product', $productName, 70)) {
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'VIRTUAL_PRODUCT_MAX_LENGTH'
            ];

            return $data;
        }

        $sql = "
            SELECT virtualName
                FROM ".TABLES['products']."
            WHERE virtualName = '$productName'
            LIMIT 1
        ";
        $result = $database->query($sql);

        if($database->numRows($result) > 0) {
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'VIRTUAL_PRODUCT_EXISTS'
            ];

            return $data;
        }

        return $data;
    }

    /**
     * get all packages grouped by package type
     * @param  INT $idCountry id for the country
     * @return Array            list of packages
     */
    public function getOptionsAndPackages($idCountry){
        $optionsManager = new PacakgeOptionsManager();

        return $optionsManager->getOptionsAndPackages($idCountry);
    }

    /**
     * update packages options and additional packages
     * @param  INT $idPackage          id for the pacakge
     * @param  String $groups             json string for list of option groups
     * @param  String $additionalPackages json string for list of additional packages
     * @return Array                     update message
     */
    public function updatePackageOptions($idPackage, $groups, $additionalPackages){
        $optionsManager = new PacakgeOptionsManager();

        return $optionsManager->updatePackageOptions($idPackage, $groups, $additionalPackages);
    }

    /**
     * get packages types
     * @return array        list of package types
     */
    public function getPackageTypes(){
        global $database;
        $extraJoin = "";

        $sql = "SELECT
                    pt.id AS idPackageType,
                    pt.packageType
                FROM ".TABLES['package_types']." pt
                $extraJoin
                ORDER BY packageType";
        $data['packageTypes'] = $database->fetchResultArray($sql);

        return $data;
    }

    public function uploadNewImage($file, $options) {
        global $database;
        $idPackage = array_key_exists('idPackage', $options) ? $options['idPackage'] : 0;
        $publicId = '';

        foreach($options as $key => $value) {
            $options[$key] = $database->escapeValue($value);
            if($key === 'name') {
                $publicId = str_replace(" ", "_", $value) . '_' . $idPackage;
            }

            if($key === 'width' || $key === 'height') {
                if($value < 1 || $value > 5000) {
                    $data['messages'][] = [
                        'code' => 'error',
                        'message' => 'TOO_BIG_VALUE_FOR_IMAGE'
                    ];

                    return $data;
                }
            }
        }

        $imageOptions = $options;
        $imageOptions["use_filename"] = true;
        if($publicId) {
            $imageOptions['public_id'] = $publicId;
        }

        if(empty($file)){
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'NO_FILE'
            ];

            return $data;
        }

        $tmpName = $file['tmp_name'];
        $uploadResponse = \Cloudinary\Uploader::upload($tmpName, $imageOptions);

        $data['imageUrl'] = array_key_exists('url', $uploadResponse) ? $uploadResponse['url'] : '';
        $data['messages'][] = [
            'code' => 'success',
            'message' => 'IMAGE_UPLOADED_SUCCESSFULLY'
        ];

        if(array_key_exists('useAsMarketPicture', $options) && $options['useAsMarketPicture']) {
            $savePhotoMessages = $this->saveCoverPhotoForPackage($idPackage, $uploadResponse['public_id']);
            $data['messages'] = array_merge($data['messages'], $savePhotoMessages['messages']);
        }

        return $data;
    }

    public function getImagesFromCdn($idCountry, $idPackage) {
        global $database;

        $cloudinaryApi = new \Cloudinary\Api;
        $images = $cloudinaryApi->resources(array("type" => "upload", "max_results" => "200"));

        if($idPackage) {
            $sql = "SELECT
                      photoPublicId AS publicId
                    FROM
                      ".TABLES['packages']."
                    WHERE id = $idPackage";
            $query = $database->query($sql);
            $result = $database->fetchArray($query);

            if($result && array_key_exists('resources', $images)) {
                foreach ($images['resources'] as $key => $imageDetails) {
                    $images['resources'][$key]['useAsMarketPicture'] = $imageDetails['public_id'] === $result['publicId'];
                }
            }
        }

        return $images;
    }

    public function saveCoverPhotoForPackage($idPackage, $publicId) {
        global $database;
        $publicId = $database->escapeValue($publicId);
        $data = [];

        if(!$idPackage) {
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'PACKAGE_ID_MISSING_IMAGE_NOT_SAVED'
            ];

            return $data;
        }

        $sql = "UPDATE ".TABLES['packages']."
                    SET photoPublicId='".$publicId."'
                WHERE id=".$idPackage;
        $result = $database->query($sql);
        if($database->affectedRows()) {
            $data['messages'][] = [
                'code' => 'success',
                'message' => 'IMAGE_SAVED_FOR_CO_MARKET'
            ];
        }

        return $data;
    }
}
