<?php
/**
 * NOTICE OF LICENSE.
 *
 * Copyright 2022 Kosmonaft.dev
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 *  @author    Alexandre DEBUSSCHÈRE <alexandre@kosmonaft.dev>
 *  @copyright 2022 - 2023 Kosmonaft.dev
 *  @license   MIT
 */

use libphonenumber\PhoneNumberType;
use libphonenumber\PhoneNumberUtil;

if (!defined('_PS_VERSION_')) {
    exit;
}

/**
 * Class MRMaxTools
 */
class MRMaxTools extends Tools
{

    /**
     * @param $data
     * @param $options
     * @param $depth
     * @return false|string
     */
    public static function jsonEncode($data, $options = 0, $depth = 512)
    {
        return json_encode($data, $options, $depth);
    }

    /**
     * @param $data
     * @param $assoc
     * @param $depth
     * @param $options
     * @return array|mixed
     */
    public static function jsonDecode($data, $assoc = false, $depth = 512, $options = 0)
    {
        return json_decode($data, $assoc, $depth, $options);
    }

    /**
     * @param string $str
     * @return string
     */
    public static function sanitizeForApiCall($str)
    {
        $str = mb_convert_encoding($str, 'HTML-ENTITIES', 'UTF-8');
        $str = preg_replace(
            ['/&szlig;/', '/&(..)lig;/', '/&([aouAOU])uml;/', '/&(.)[^;]*;/'],
            ['ss', '$1', '$1e', '$1'],
            $str
        );
        $str = str_replace('_', '/', $str);
        $str = preg_replace('/[\x00-\x1F\x21-\x2E\x3A-\x3F\x5B-\x60\x7B-\x7F]/', ' ', $str);

        return $str;
    }

    /**
     * @return array
     * @throws PrestaShopDatabaseException
     */
    public static function getTables()
    {
        return array_map(function ($table) {
            return reset($table);
        }, Db::getInstance()->executeS('SHOW TABLES'));
    }

    /**
     * @param string $table
     * @param bool $add_prefix
     * @return bool
     * @throws PrestaShopDatabaseException
     */
    public static function tableExists($table, $add_prefix = true)
    {
        return in_array(
            sprintf(
                '%s%s',
                $add_prefix ? _DB_PREFIX_ : '',
                (string)$table
            ),
            self::getTables()
        );
    }

    /**
     * @param string $table
     * @param string $name
     * @param bool $add_prefix
     * @return bool
     * @throws PrestaShopDatabaseException
     */
    public static function tableHasColumn($table, $name, $add_prefix = true)
    {
        if ($add_prefix) {
            $table = _DB_PREFIX_.$table;
        }

        $column = Db::getInstance()->executeS('
            SHOW COLUMNS FROM `'.pSQL($table).'` LIKE "'.pSQL($name).'"
        ');

        return count(
            array_map(function ($table) {
                return reset($table);
            }, $column)
        ) === 1;
    }

    /**
     * @param string $table
     * @param string $name
     * @param bool $add_prefix
     * @return array|bool|object|null
     */
    public static function getTableColumnMetadata($table, $name, $add_prefix = true)
    {
        if ($add_prefix) {
            $table = _DB_PREFIX_.$table;
        }

        return Db::getInstance()->getRow('
            SELECT *
            FROM `information_schema`.`COLUMNS`
            WHERE `TABLE_NAME` = "'.pSQL($table).'"
            AND `COLUMN_NAME` = "'.pSQL($name).'"
        ');
    }

    /**
     * @param int $id_address_new
     * @param int $id_cart
     * @result bool
     */
    public static function updateIdAddressDeliveryForCustomizations($id_address_new, $id_cart)
    {
        $queries = [];
        $queries[] = 'UPDATE `'._DB_PREFIX_.'cart_product`
            SET `id_address_delivery` = '.(int)$id_address_new.'
            WHERE `id_cart` = '.(int)$id_cart;
        $queries[] = 'UPDATE `'._DB_PREFIX_.'customization`
                SET `id_address_delivery` = '.(int)$id_address_new.'
                WHERE `id_cart` = '.(int)$id_cart;

        return array_reduce($queries, function ($result, $query) {
            $result += Db::getInstance()->execute($query);
            return $result;
        }, true);
    }

    /**
     * @param string $number
     * @param string $iso_code
     * @return string
     */
    public static function formatPhoneNumber($number, $iso_code)
    {
        $phone_util = PhoneNumberUtil::getInstance();

        try {
            $phone = $phone_util->parse($number, $iso_code);
            if ($phone_util->isValidNumberForRegion($phone, $iso_code)) {
                return sprintf(
                    '%s%s',
                    $iso_code == 'FR' ? '0' : ('+'.$phone->getCountryCode()),
                    $phone->getNationalNumber()
                );
            }
        } catch (Exception $exception) {
            Tools::dieOrLog($exception->getMessage(), false);
        }

        $phone = $phone_util->getExampleNumberForType($iso_code, PhoneNumberType::MOBILE);

        return sprintf(
            '%s%s',
            $iso_code == 'FR' ? '0' : ('+'.$phone->getCountryCode()),
            $phone->getNationalNumber()
        );
    }

    /**
     * @param string $cache_key
     * @param string $id_pickup
     * @param mixed $default
     * @return array
     */
    public static function getPickupFromCache($cache_key, $id_pickup, $default = [])
    {
        $cache = new MRMaxCache();

        if (!$cache->has($cache_key)) {
            MRMaxTools::log(sprintf(
                '%s: does not have key "%s", default returned instead (%s)',
                __METHOD__,
                $cache_key,
                MRMaxTools::jsonEncode($default)
            ));
            return $default;
        }

        $pickup = array_reduce(
            MRMaxTools::jsonDecode($cache->get($cache_key), true),
            function ($acc, $cur) use ($id_pickup) {
                if ($cur['Num'] == $id_pickup) {
                    return $cur;
                }
                return $acc;
            },
            []
        );

        if (!is_array($pickup) || !count($pickup)) {
            MRMaxTools::log(sprintf(
                '%s: Cannot find pickup "%s" in cache, returning default instead (%s)',
                __METHOD__,
                $id_pickup,
                MRMaxTools::jsonEncode($default)
            ));
            return $default;
        }

        return $pickup;
    }

    /**
     * @return false|int|string
     */
    public static function getCarrierMapByContext()
    {
        $carrier_map = Configuration::get('MRMAX_CARRIERS_MAP');
        if ($carrier_map == MRMaxForm::CARRIER_MAP && Context::getContext()->isMobile()) {
            $carrier_map = MRMaxForm::CARRIER_LIST;
        }

        return $carrier_map;
    }

    /**
     * @param float $weight
     * @return float
     * @throws PrestaShopException
     */
    public static function convertToKg($weight)
    {
        $unit = Tools::strtolower(Configuration::get('PS_WEIGHT_UNIT'));
        if ($unit == 'kg') {
            return $weight;
        }

        switch ($unit) {
            case 'mg':
                $weight /= 1000000;
                break;

            case 'dg':
                $weight /= 10000;
                break;

            case 'g':
                $weight /= 1000;
                break;

            case 'dag':
                $weight /= 100;
                break;

            case 'hm':
                $weight /= 10;
                break;

            default:
                throw new PrestaShopException(sprintf(
                    'Unit %s is unknown, can not convert it to Kg.',
                    $unit
                ));
        }

        return $weight;
    }

    /**
     * @param string $message
     * @return bool
     */
    public static function log($message = '')
    {
        if (class_exists('PrestaShopLogger')) {
            return PrestaShopLogger::addLog($message);
        }

        return Tools::dieOrLog($message, false);
    }
}
