<?php
namespace TwigExtension;

use Twig\Extension\AbstractExtension;
use PDO;
use Twig\TwigFunction;
use Exception;
use Deprecated;
use DateTime;
use Entity\Model\Enum\ILabelized;
use Modules\GlobalApplication\Repository\DocumentsRattaches;
use stdClass;
use Entity\Model\OxalinkUser;
use Entity\Model\Paravrac;
use Helpers\Outil\Sql;
use Helpers\Session\ParseBase1;
use Helpers\Trad\ITrad;
use Modules\Evaluation\Data\Parametre;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use Psr\Container\ContainerInterface;
use Skeleton\Router;
use Helpers\Outil\Date;
use Helpers\Parametres\Constante;
use Helpers\Trad\Trad;
use Helpers\ChargeDocument\ChargeDocument;
use Helpers\Log\LogSysteme;
use Helpers\Fichier\Fichier;
use Helpers\Outil\EnumHelper;
use Helpers\Outil\Outils;
use Helpers\Outil\Roles;
use Helpers\Outil\XmlHelper;
use Modules\GlobalApplication\Repository\LicenceOxalys;
use Symfony\Component\String\Slugger\SluggerInterface;
use Twig\TwigFilter;

class OutilExtension extends AbstractExtension
{
    protected Parametre $parameter;

    private readonly ITrad $trad;
    private readonly OxalinkUser $oxalinkUser;
    
    public function __construct(protected PDO $pdo, protected ContainerInterface $contenaire, protected Router $router)
    {
        $this->parameter =  new Parametre($this->pdo);
        $this->oxalinkUser = new OxalinkUser($this->pdo);
        $this->trad = $contenaire->get(ITrad::class);
    }
    
    /**
     * Summary of getFunctions
     * @return TwigFunction[]
     */
    public function getFunctions()
    {
        return [
            new TwigFunction(
                'uid',
                $this->uid(...),
                ['is_safe' => ['html']]
            ),
            new TwigFunction('boolSql', $this->boolSql(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('getAll', $this->getAll(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('baseUrl', $this->baseUrl(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('base', $this->base(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('googleMap', $this->googleMap(...), [
                'is_safe' => [
                    'html'
                ]
            ]),

            new TwigFunction('include_file', $this->include_file(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('skinCode', $this->skinCode(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('spliter', $this->spliter(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('piedDePage', $this->piedDePage(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('titleHtml', $this->titleHtml(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('var_dump', $this->var_dump(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('remise', $this->remise(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('shortLibelle', $this->shortLibelle(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('shortLibelleV2', $this->shortLibelleV2(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('keyPathliste', $this->keyPathliste(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('deleteSession', $this->deleteSession(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('getSession', $this->getSession(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('labelCreateur', $this->labelCreateur(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('tradBuild1Variable', $this->tradBuild1Variable(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('iconeDocAtt', $this->iconeDocAtt(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('scriptReporting', $this->scriptReporting(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
                
            new TwigFunction('str_replace', $this->str_replace(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('affichageImageOxadata', $this->affichageImageOxadata(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('displayImgOxadata', $this->displayImgOxadata(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('tradDatePickers', $this->tradDatePickers(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('str_pad', $this->str_pad(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('hover_action', $this->hover_action(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('convertIDToInput', $this->convertIDToInput(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction('buttonUpDown', $this->buttonUpDown(...), [
                'is_safe' => [
                    'html'
                ]
            ]),
            new TwigFunction(
                'colon',
                $this->colon(...),
                ['is_safe' => ['html']]
            ),
            new TwigFunction(
                'strBuild',
                $this->strBuild(...),
                ['is_safe' => ['html']]
            ),
            new TwigFunction(
                'generateDataTooltips',
                $this->generateDataTooltips(...),
                ['is_safe' => ['html']]
            ),
            new TwigFunction(
                'attr',
                $this->attr(...),
                ['is_safe' => ['html']]
            ),
            new TwigFunction(
                'entityLabel',
                [self::class, 'entityLabel'],
            ),
            new TwigFunction(
                'hastutilLabel',
                $this->hastutilLabel(...),
                ['is_safe' => ['html']]
            ),
            new TwigFunction(
                'ajaxPopup',
                $this->ajaxPopup(...),
                ['is_safe' => ['html']]
            ),
            new TwigFunction(
                'qteByUnite',
                $this->qteByUnite(...),
                ['is_safe' => ['html']]
            ),
            new TwigFunction(
                'getConst',
                $this->getConst(...),
                ['is_safe' => ['html']]
            ),
            new TwigFunction(
                'hasRight',
                $this->hasRight(...),
                ['is_safe' => ['html']]
            ),
            new TwigFunction(
                'connexionImage',
                $this->connexionImage(...),
                ['is_safe' => ['html']]
            ),
            new TwigFunction(
                'updateImageFromSource',
                $this->updateImageFromSource(...),
                ['is_safe' => ['html']]
            ),
            new TwigFunction(
                'attrJs',
                $this->attrJs(...),
                ['is_safe' => ['html']]
            ),
            new TwigFunction(
                'qteByDecimal',
                $this->qteByDecimal(...),
                ['is_safe' => ['html']]
            ),
            new TwigFunction(
                'oxalinkUserStatut', // TODO : too specific, move somewhere elese
                $this->oxalinkUserStatut(...),
                ['is_safe' => ['html']]
            ),
            new TwigFunction(
                'isSuperAdmin',
                $this->isSuperAdmin(...)
            ),
            new TwigFunction(
                'licenceOxa',
                $this->licenceOxa(...)
            ),
        ];
    }
    
    /**
     * Summary of getFilters
     * @return array{addslashes: TwigFilter, attr: TwigFilter, baseUrl: TwigFilter, boolSql: TwigFilter, boolTrad: TwigFilter, convertFloat: TwigFilter, convertInt: TwigFilter, convertStr: TwigFilter, convert_encoding: TwigFilter, dateOrigin: TwigFilter, dateTrad: TwigFilter, datetime: TwigFilter, excelColLabel: TwigFilter, firstLettre: TwigFilter, heureTrad: TwigFilter, importFormatLabel: TwigFilter, jsTemplate: TwigFilter, json: TwigFilter, lengthTable: TwigFilter, lienExterieur: TwigFilter, majF00: TwigFilter, moisHistoFournisss: TwigFilter, montantAbreger: TwigFilter, numberFormat: TwigFilter, paramDateFormat: TwigFilter, prix: TwigFilter, prixUnitaire: TwigFilter, qte: TwigFilter, replaceInID: TwigFilter, setObj: TwigFilter, slug: TwigFilter, substr: TwigFilter, trad: TwigFilter, tva: TwigFilter, upperValue: TwigFilter}
     */
    public function getFilters()
    {
        return [
            'upperValue' => new TwigFilter('upperValue', $this->upperValue(...)),
            //'withoutParenthese' => new TwigFilter('withoutParenthese', array($this, 'withoutParenthese')),
            'prix' =>  new TwigFilter('prix', $this->prix(...)),
            'prixUnitaire' =>  new TwigFilter('prixUnitaire', $this->prixUnitaire(...)),
            'numberFormat' =>  new TwigFilter('numberFormat', $this->numberFormat(...)),
            'tva' =>  new TwigFilter('tva', $this->tva(...)),
            'qte' =>  new TwigFilter('qte', $this->qte(...)),
            'dateTrad' => new TwigFilter('dateTrad', $this->dateTrad(...)),
            'dateOrigin' => new TwigFilter('dateOrigin', $this->dateOrigin(...)),
            'heureTrad' => new TwigFilter('heureTrad', $this->heureTrad(...)),
            'paramDateFormat' => new TwigFilter('paramDateFormat', $this->paramDateFormat(...)),
            'boolTrad' => new TwigFilter('boolTrad', $this->boolTrad(...)),
            'boolSql' => new TwigFilter('boolSql', $this->boolSql(...)),
            'firstLettre' => new TwigFilter('firstLettre', $this->firstLettre(...)),
            'trad' => new TwigFilter('trad', $this->trad(...)),
            'baseUrl' => new TwigFilter('baseUrl', $this->baseUrl(...)),
            'convert_encoding' => new TwigFilter('convert_encoding', $this->convert_encoding(...)),
            'lengthTable' => new TwigFilter('lengthTable', $this->lengthTable(...)),
            'substr' => new TwigFilter('substr', $this->substr(...)),
            'lienExterieur' => new TwigFilter('lienExterieur', $this->lienExterieur(...)),
            'convertInt' => new TwigFilter('convertInt', $this->convertInt(...)),
            'convertStr' => new TwigFilter('convertStr', $this->convertStr(...)),
            'convertFloat' => new TwigFilter('convertFloat', $this->convertFloat(...)),
            'majF00' => new TwigFilter('convertStr', $this->convertStr(...)),
            'moisHistoFournisss' => new TwigFilter('moisHistoFournisss', $this->moisHistoFournisss(...)),
            'replaceInID' => new TwigFilter('replaceInID', $this->replaceInID(...)),
            'addslashes' => new TwigFilter('addslashes', $this->addslashes(...)),
            'montantAbreger' => new TwigFilter('montantAbreger', $this->montantAbreger(...)),
            
            'attr' => new TwigFilter('attr', $this->attr(...), ['is_safe' => ['html']]),
            'json' => new TwigFilter('json', $this->json(...), ['is_safe' => ['html']]),
            'jsTemplate' => new TwigFilter('jsTemplate', $this->jsTemplate(...), ['is_safe' => ['html']]),

            'importFormatLabel' => new TwigFilter('importFormatLabel', $this->importFormatLabel(...)),
            'excelColLabel' => new TwigFilter('excelColLabel', $this->excelColLabel(...)),
            'slug' => new TwigFilter('slug', $this->slug(...)),
            'setObj' => new TwigFilter('setObj', $this->setObj(...)),

            'datetime' => new TwigFilter('datetime', $this->datetime(...)),
            'datetime_date' => new TwigFilter('datetime_date', $this->datetimeDate(...)),
            'values' => new TwigFilter('values', $this->values(...)),// array_values
            'enumToChoice' => new TwigFilter('enumToChoice', $this->enumToChoice(...)),
            'enumToLabel' => new TwigFilter('enumToLabel', $this->enumToLabel(...)),
        ];
    }

    /**
     * Summary of getAll
     * @return bool
     */
    public function getAll()
    {
        return Roles::GetALL();
    }
    
    /**
     * Summary of shortLibelle
     * @param mixed $value
     * @param mixed $taille
     * @param mixed $position
     * @param mixed $oldTooltips
     * @param mixed $ajaxPopup
     * @return string
     */
    public function shortLibelle($value, $taille = 30, $position = 'top-right', $oldTooltips = false, $ajaxPopup = true) : string
    {
        // TODO : retire or refactor $oldTooltips
        if ($oldTooltips) {
            if (mb_strlen((string) $value)>$taille) {
                return '<span title="'.$value.' ">'.mb_substr((string) $value, 0, $taille).'...</span>';
            } else {
                return $value??'';
            }
        }
        
        if (mb_strlen((string) $value)>$taille) {
            return '<span class="white-tooltip" '.($ajaxPopup? 'style="cursor:pointer;" onClick="
            ajaxPopup(\''.$this->router->generateUri('Ajax.outil.contentText').'\',
             \'\', \''.urlencode((string) $value).'\');"':'').'>
                    	'.mb_substr((string) $value, 0, $taille).' ...
                    	<span class="infos_tooltip '.$position.' ">
            			<span class="tooltip-arrow"></span>
                            '.$value.'              			
                    	</span>
                	</span>';
        } else {
            return $value??'';
        }
    }

    /**
     * @param string $value
     * @param int $taille
     * @param string $position
     * @return string
     */
    public function shortLibelleV2(string $value, $taille = 30, string $position = 'top'):string
    {
        if (mb_strlen((string) $value)>$taille) {
            return '<span style="cursor:pointer;" onClick="ajaxPopup(\''.$this->router->generateUri('Ajax.outil.contentText').'\',\'\', \''.urlencode((string) $value).'\');"
                        data-container="#div_tooltip" data-toggle="tooltip"  data-placement="'.$position.'" data-html="true" data-original-title="'.$value.'">
                        '.mb_substr((string) $value, 0, $taille).' ...
                    </span>';
        } else {
            return $value;
        }
    }

    /**
     * @param array<string> $routeParam
     * @param array<string> $popupParam
     */
    public function ajaxPopup(
        string $title,
        string $route,
        array $routeParam = [],
        array $popupParam = []
        // TODO : add params ?
    ) :string {
        //function ajaxPopup(path, title, divCode, divLibelle, params, width_popup)
        return 'ajaxPopup('
            . implode(',', [
                json_encode($this->router->generateUri($route, $routeParam)),
                json_encode($title),
                '""',
                '""',
                json_encode($popupParam)
            ])
            . ')';
    }
    
    /**
     * Summary of moisHistoFournisss
     * @param mixed $value
     * @return string
     */
    public function moisHistoFournisss($value) : string
    {
        return Date::convertDateEnMois($value, $this->contenaire->get(ITrad::class));
    }
    
    /**
     * Summary of var_dump
     * phpcs:disable
     * @param mixed $value
     * @return void
     */
    public function var_dump($value): void
    {
        echo '<pre>';
        var_dump($value);
        echo '</pre>';
    }
    
    /**
     * Summary of remise  
     * phpcs:enable
     * @param mixed $value
     * @param mixed $remise
     */
    public function remise($value, $remise = ''): mixed
    {
        if ($remise) {
            return $value - $value*$remise/100;
        } else {
            return $value;
        }
    }
    
    /**
     * Summary of baseUrl
     * pour recuperer le baseUrl de OXAPROCV2
     * @param string $value
     * @return string
     */
    public function baseUrl(string $value = '')
    {
        return $this->contenaire->get('baseUrl').$value;
    }
    
    /**
     * Summary of spliter
     * @return string
     */
    public function spliter()
    {
        return Constante::spliter;
    }

    /**
     * @return float|string|int
     */
    public function getConst(string $value)
    {
        $constant = new Constante();
        return $constant->getConst($value);
    }
    
    /**
     * Summary of base
     * pour recuperer le baseUrl de l'ancien OXAPROC
     * @param string $value
     * @return string
     */
    public function base(string $value = '')
    {

        return $this->contenaire->get('base').$value;
    }
    
    /**
     * Summary of skinCode
     * @return string
     */
    public function skinCode()
    {
        // $baseURL = new OutilExtension($this->pdo, $this->contenaire, $this->router);
        $baseURL = $this->contenaire->get('baseUrl');

        $paravrac = new Paravrac($this->pdo);
        $post['colorClient1'] = $paravrac->find(['TYPEPARV' => 'OXAPROC', 'CODEPARV' => 'COLOR_CLIENT_1'])['LIB01PARV']??'';
        $post['colorClient2'] = $paravrac->find(['TYPEPARV' => 'OXAPROC', 'CODEPARV' => 'COLOR_CLIENT_2'])['LIB01PARV']??'';

        $post['colorTable'] = $paravrac->find(['TYPEPARV' => 'OXAPROC', 'CODEPARV' => 'COLOR_TABLE'])['LIB01PARV']??'';

        if (!$post['colorClient1']) {
            $post['colorClient1'] = '#89ccc5';
        }

        if (!$post['colorClient2']) {
            $post['colorClient2'] = '#0a264e';
        }

        if ($post['colorTable'] != '2') {
            $mainColor = '#7C7C7C';
            $textColor = '#fff';

            $triUp = 'url('.$baseURL.'/IconeV2/Misc/trier_up.png)';
            $triDown = 'url('.$baseURL.'/IconeV2/Misc/trier_down.png)';
            $tri = 'url('.$baseURL.'/IconeV2/Misc/trier_tableau.png)';

            $image = 'url(../IconeV2/Global/add-white.svg)';
        } else {
            $mainColor = '#fff';
            $textColor = '#000';

            $triUp = 'url('.$baseURL.'/IconeV2/Misc/trier_up_black.png)';
            $triDown = 'url('.$baseURL.'/IconeV2/Misc/trier_down_black.png)';
            $tri = 'url('.$baseURL.'/IconeV2/Misc/trier_tableau_black.png)';

            $image = 'url(../IconeV2/MenuContextuel/create.svg)';
        }
        
        $_SESSION['SESSION_SKIN_CODE_V2'] = $post['colorClient1'];

        return '<style>:root{
            --main-color-client1    : '.$post['colorClient1'].';
            --main-color-opac15     : '.$post['colorClient1'].'26;
            --main-color-opac25     : '.$post['colorClient1'].'3F;
            --main-color-opac50   : '.$post['colorClient1'].'80;

            --main-color-client2 : '.$post['colorClient2'].';
            --main-color-client2-opac50 : '.$post['colorClient2'].'80;


            --header-main-color      : '.$mainColor.';
            --header-text-color      : '.$textColor.';
            --image-add              : '.$image.';

            --triUp : '.$triUp.';
            --triDown : '.$triDown.';
            --tri : '.$tri.';
        }</style>';
    }


    /**
     * Summary of googleMap
     * @param mixed $adresse
     * @param mixed $ville
     * @return string
     */
    public function googleMap($adresse, $ville)
    {
        $adresse = str_replace(" ", "+", $adresse);
        
        return "https://www.google.com/maps/place/$adresse,$ville";
    }
    
    /**
     * Summary of include_file
     * phpcs:disable
     * @param mixed $path
     * @return void
     */
    public function include_file($path): void
    {
        include $path;
    }
    
    /**
     * Summary of convert_encoding
     * @param mixed $value
     * @param mixed $val
     * @param mixed $text
     */
    public function convert_encoding($value, $val, $text): mixed
    {
        unset($val, $text);
        return$value;
    }
    // phpcs:enable

    /**
     * Summary of upperValue
     * @param mixed $value
     * @return string
     */
    public function upperValue($value) : string
    {
        $str = strtoupper((string) $value);
        return 'transformated-' . $str;
    }
    
    /**
     * Summary of trad
     * @param mixed $value
     * @return string
     */
    public function trad($value)
    {
        if (!is_string($value)) {
            LogSysteme::prefixErrorLog(
                (new Exception('Expected string got "'.gettype($value).'"'))->__toString()
            );
            return '';
        }
        return $this->trad->t($value);
    }

    /**
     * @param array<string> $param
     */
    public function strBuild(string $template, array $param) :string
    {
        return Trad::strBuild($template, ...$param);
    }
    
    /**
     * Summary of colon
     * @return string
     */
    public function colon() :string
    {
        return $this->contenaire->get(ITrad::class)->colon();
    }
    
    /**
     * Summary of tradBuild1Variable
     * @param mixed $value
     * @param mixed $variable
     * @return string
     */
    public function tradBuild1Variable($value, $variable)
    {
        return Trad::strBuild($value, $variable);
    }
    
    /*public function withoutParenthese($value)
    {
        return substr($value, ($p = strpos($value, '(')+1), strrpos($value, ')')-$p);
    }*/
    
    /**
     * Summary of prixUnitaire
     * @param mixed $value
     * @return string
     */
    public function prixUnitaire($value)
    {
        return Outils::formatPrixUnitaire($value);
    }
    
    /**
     * Summary of prix
     * @param mixed $value
     * @return string
     */
    public function prix($value)
    {
        return str_replace(' ', "\xc2\xa0", Outils::formatMontant($value));
    }
    
    /**
     * Summary of numberFormat
     * @param mixed $value
     * @return string
     */
    public function numberFormat($value)
    {
        return  chunk_split((string) $value, 3, ' ');
    }
    
    /**
     * Summary of tva
     * @param mixed $value
     * @return string
     */
    public function tva($value)
    {
        return Outils::formatMontant($value);
    }

    /**
     * @deprecated 'use qteByUnite instead'
     * @param mixed $value
     * @return mixed
     */
    public function qte($value)
    {
        $prixDeBase = str_replace(',', '.', $value);
        
        if (!is_numeric($prixDeBase)) {
            return $value;
        }
        if ($prixDeBase) {
            $value = number_format((float)$prixDeBase, constante::qteDecimal, '.', ' ');
        } else {
            $value = '0.000';
        }
                
        return  $value;
    }

    /**
     * Summary of qteByDecimal
     * @param string $value
     * @param ?string $decimal
     * @return float|string
     */
    public function qteByDecimal(string $value, ?string $decimal = null):float|string
    {
        if (!$decimal && $decimal !== '0') {
            $decimal = Constante::qteDecimal;
        } elseif ($decimal>3) {
            $decimal = Constante::qteDecimal;
        }

        $prixDeBase = str_replace(',', '.', $value);
        
        if (!is_numeric($prixDeBase)) {
            return $value;
        }
        if ($prixDeBase) {
            $value = number_format((float)$prixDeBase, (int)$decimal, '.', ' ');
        } else {
            $value = '0.000';
        }
                
        return  $value;
    }

    /**
     * @param float|string|null $qte
     * @param string|null $unite
     */
    public function qteByUnite($qte, $unite) : string
    {
        return Outils::qteByUnite($this->pdo, $qte, $unite);
    }
    
    /**
     * Summary of montantAbreger
     * @param mixed $value
     * @return float|string
     */
    public function montantAbreger($value)
    {
        $prixDeBase = str_replace(',', '.', $value);

        if ($prixDeBase) {
            $prixDeBase = ((float)$prixDeBase/1000);
            $value = number_format($prixDeBase, 2, '.', ' ');
        } else {
            $value = '0.00';
        }
        
        return $value;
    }
    
    /**
     * Summary of dateTrad
     * @param mixed $date
     * @return string
     */
    public function dateTrad($date) : string
    {
        if ($date == '') {
            return '';
        }
        
        $dateObjet = new Date();
        $date = $dateObjet->dateConverte($date, null);
        
        if ($date=='  /  /    ') {
            return '';
        }
            
        return $date;
    }

    /**
     * @param mixed $date
     */
    public function datetime($date) : string
    {
        return Date::formatDatetime($date);
    }
    
    /**
     * @param mixed $date
     */
    public function datetimeDate($date) : string
    {
        return Date::formatDatetime($date, false);
    }

    /**
     * Summary of dateOrigin
     * Cette fonction permet de retourner la date passer en parametre sans les caractères spéciaux
     * @param mixed $date
     * @return string
     */
    public function dateOrigin($date) : string
    {
        $year=substr((string) $date, 6, 4);
        $month=substr((string) $date, 3, 2);
        $day=substr((string) $date, 0, 2);
        
        $dateConvertie = $year."".$month."".$day;
        
        return str_replace('/', '', $dateConvertie);
    }

    /**
     * Summary of heureTrad
     * @param mixed $heuresend
     * @return string
     */
    public function heureTrad($heuresend) : string
    {
        if ($heuresend == '') {
            return '';
        }

        return Date::heureTrad($heuresend);
    }
    
    /**
     * Summary of paramDateFormat
     * @param ?string $value
     * @return string
     */
    public function paramDateFormat(?string $value = null)
    {
        $date = str_replace('j', 'd', (string)$_SESSION['DATE_FORMAT']);
        $date = str_replace('a', 'y', $date);
            
        return str_replace('yyyy', 'yy', $date);
    }
    
    /**
     * Summary of boolTrad
     * @param mixed $value
     * @return string
     */
    public function boolTrad($value) : string
    {
        return $this->trad->displayBool($value);
    }

    /**
     * Summary of boolSql
     * @param mixed $info
     * @return int
     */
    public function boolSql($info) : int
    {
        return Sql::boolSql($info);
    }
    
    /**
     * Summary of firstLettre
     * @param mixed $value
     * @return string
     */
    public function firstLettre($value) : string
    {
        return substr((string) $value, 0, 1);
    }

    /**
     * @param mixed $table
     * @return int
     */
    public function lengthTable($table)
    {
        if ($table) {
            return count($table);
        }
        return 0;
    }
    
    /**
     * Summary of substr
     * @param mixed $value
     * @param mixed $first
     * @param mixed $length
     * @return string
     */
    public function substr($value, $first, $length = null)
    {
        if ($length==null || $length=='') {
            $text = substr((string) $value, $first);
        } else {
            $text = substr((string) $value, $first, $length);
        }
        
        return $text;
    }

    /**
     * Summary of lienExterieur
     * @param string $url
     */
    public function lienExterieur($url): string
    {
        if (filter_var($url, FILTER_VALIDATE_URL)) {
            return $url;
        } else {
            return 'http://'.$url;
        }
    }

    /**
     * Summary of titleHtml
     * @return string
     */
    public function titleHtml()
    {
        return "<title>.: Oxalys :. - ".($this->contenaire->get('version'))." (".((new ParseBase1())->getEnvName()).")</title>";
    }
    
    /**
     * Summary of piedDePage
     * @return string
     */
    public function piedDePage()
    {
        $support = ($urlSupport = sql::paravrac($this->pdo, 'OXAPROC', 'SUPPORT_URL')) != ''
            ? '<a href="' . $urlSupport . '">' . $this->trad->t('Support') . '</a>'
            : '';

        $contact = ($urlContact = sql::paravrac($this->pdo, 'OXAPROC', 'CONTACT_URL')) != ''
            ? '<a href="' . $urlContact . '">' . $this->trad->t('Contact') . '</a>'
            : '';

        return "<span>".($this->contenaire->get('fullVersion'))." | Code client N°".($_SESSION['Session_NumClient']??'')." | ".($_SESSION['parametrage']['NOM_SITE_INSTALL_HAP']??'')." | ".($this->contenaire->get('copyright'))." |  ENV : ".((new ParseBase1())->getEnvName())." | ".substr((string) $_SERVER['COMPUTERNAME'], 0, 4)."OXA" . " | " . $support . " | " . $contact . "</span>";
    }
    
    /**
     * Summary of convertInt
     * @param mixed $value
     * @return int
     */
    public function convertInt($value)
    {
        return (int) $value;
    }
    
    /**
     * Summary of convertStr
     * @param mixed $value
     * @return string
     */
    public function convertStr($value)
    {
        return (string) $value;
    }
    
    /**
     * Summary of convertFloat
     * @param mixed $value
     * @return float
     */
    public function convertFloat($value)
    {
        return (float) str_replace(' ', '', $value);
    }

    /**
     * Summary of replaceInID
     * @param mixed $value
     * @return string|string[]|null
     */
    public function replaceInID($value)
    {
        // $specCaract = array("&", "?", ">", "|", "*", "^", "<", "'", '"', "%", ".","_",'+',"-", '(',')', '@', '[', ']', ' ');
        $specCaract = ["&", "?", ">", "|", "*", "^", "<", "'", '"', "%", ".",'+',"-", '(',')', '@', '[', ']', ' '];

        foreach ($specCaract as $caract) {
            $value = str_replace($caract, '', $value);
        }

        return $this->suppr_accents($value);
    }
    
    /**
     * Summary of suppr_accents
     * phpcs:disable
     * @param mixed $str
     * @param mixed $encoding
     * @return string[]|string|null
     */
    public function suppr_accents($str, $encoding = 'utf-8')
    {
        // phpcs:enable
        // transformer les caractères accentués en entités HTML
        $str = htmlentities((string) $str, ENT_NOQUOTES, $encoding);
    
        // remplacer les entités HTML pour avoir juste le premier caractères non accentués
        // Exemple : "&ecute;" => "e", "&Ecute;" => "E", "à" => "a" ...
        $str = preg_replace('#&([A-za-z])(?:acute|grave|cedil|circ|orn|ring|slash|th|tilde|uml);#', '\1', $str);
    
        // Remplacer les ligatures tel que : , Æ ...
        // Exemple "œ" => "oe"
        $str = preg_replace('#&([A-za-z]{2})(?:lig);#', '\1', (string) $str);
        // Supprimer tout le reste
        $str = preg_replace('#&[^;]+;#', '', (string) $str);
    
        return $str;
    }

    /**
     * Summary of keyPathliste
     * @param mixed $str
     * @param mixed $type
     */
    public function keyPathliste($str, $type = 'path'): mixed
    {
        if ($type == 'titre') {
            return $this->parameter->keyPathListeTitre($str, $this->trad);
        }
        
        return $this->parameter->keyPathliste($str, $type);
    }

    /**
     * Summary of deleteSession
     * @param mixed $value
     * @return void
     */
    public function deleteSession($value): void
    {
        unset($_SESSION[$value]);
    }

    /**
     * Summary of getSession
     * @param mixed $value
     */
    public function getSession($value): mixed
    {
        return $_SESSION[$value] ?? null;
    }

    /**
     * @param array<mixed> $param
     */
    public function attrJs(string $function, array $param) :string
    {
        return htmlspecialchars(
            $function
            . '('
                . join(',', array_map(fn($s): string => (string) json_encode($s), $param))
            . ')'
        );
    }
    
    /**
     * Summary of labelCreateur
     * @param mixed $params
     * @return string
     */
    public function labelCreateur($params)
    {
        return '<label class="createur" '
            . $this->attr([
                'for' => 'input_'.$params['name'],
                'id' => 'label_'.$params['name'],
                'onclick' => 'changeCodeLibelle('
                    . implode(',', [
                        json_encode('input_'.$params['name']),
                        json_encode($params['code']),
                        json_encode('text_'.$params['name']),
                        json_encode($params['libelle']. ' ('.$params['code'].')'),
                    ])
                    .');'
                    . ($params['form'] ?? ''
                        ? '$(\'#'.($params['form']??'').'\').submit();'
                        : ''
                    ),
            ]) . '>'
            . htmlentities((string) $params['titre'])
            . '&nbsp;<span class="white-tooltip icon-user">
                    <span class="infos_tooltip '.$params['orientation'].'">
                        '.htmlentities((string) $params['textTooltip']).'
                    </span>
                </span>
            </label>';
    }
    
    /**
     * Summary of iconeDocAtt
     * @param mixed $NO_DA
     * @param mixed $id
     * @param mixed $path
     * @return string
     */
    public function iconeDocAtt($NO_DA, $id, $path)
    {
        $traduction = $this->contenaire->get(ITrad::class);
        switch ($NO_DA) {
            case 'FAC_ENT':
                $id = str_pad((string) $id, 9, '0', STR_PAD_LEFT);
                $text = $traduction->t('Présence de documents rattachés pour cette facture. Cliquer pour les visualiser');
                break;
            case 'DA_ENTET':
                $id = str_pad((string) $id, 9, '0', STR_PAD_LEFT);
                $text = $traduction->t('Présence de documents rattachés pour cette demande d\'achat. Cliquer pour les visualiser');
                break;
            case 'FAMILLE':
                $text = $traduction->t('Présence de documents rattachés pour cette famille. Cliquer pour les visualiser');
                break;
            default:
                $text = $traduction->t('Présence de documents rattachés. Cliquer pour les visualiser');
                break;
        }
        $docRatt = new DocumentsRattaches($this->pdo);
        $listDocAtt = $docRatt->getDocRattaches($NO_DA, $id);

        $html = '';
        if (is_array($listDocAtt) && count($listDocAtt)>0) {
            $html = '<a href="'.$path.'" class="documents_attaches white-tooltip">
                        <span class="infos_tooltip top-left">
                            <span class="tooltip-arrow"></span>'.
                            $text.
                        '</span>
                    </a>';
        }
                
        return $html;
    }

    /**
     * Summary of addslashes
     * @param mixed $value
     * @return string
     */
    public function addslashes($value)
    {
        return addslashes((string) $value);
    }

    /**
     * Summary of scriptReporting
     */
    public function scriptReporting(): void
    {
        // if(isset($_SESSION['Matomo']['ALPHA1']) && $_SESSION['Matomo']['ALPHA1']!='')
        // {
        //     return "
        //         <script>
                
        //             var _paq = _paq || [];
        //             _paq.push([function() {
        //                 var self = this;
        //                 function getOriginalVisitorCookieTimeout() {
        //                     var now = new Date(),
        //                     nowTs = Math.round(now.getTime() / 1000),
        //                     visitorInfo = self.getVisitorInfo();
        //                     var createTs = parseInt(visitorInfo[2]);
        //                     var cookieTimeout = 33696000; // 13 mois en secondes
        //                     var originalTimeout = createTs + cookieTimeout - nowTs;
        //                     return originalTimeout;
        //                 }
        //                 this.setVisitorCookieTimeout( getOriginalVisitorCookieTimeout() );
        //             }]);
                    
        //             _paq.push(['trackPageView']);
        //             _paq.push(['enableLinkTracking']);
        //             (function() {
        //                 var u='//".$_SESSION['Matomo']['ALPHA1']."/';
        //                 _paq.push(['setTrackerUrl', u+'piwik.php']);
        //                 _paq.push(['setSiteId', '".$_SESSION['Matomo']['ALPHA2']."']);
        //                 var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
        //                 g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
        //             })();
        //             </script>
        //     ";
        // }
    }
    /**
     * Summary of affichageImageOxadata
     * @param mixed $path
     * @return string
     */
    public function affichageImageOxadata($path)
    {
        $chargeDocument = new ChargeDocument();
        return "<img src='".$chargeDocument->chargeDoc($this->pdo, $path, 'img', true)."'/>";
    }
    /**
     * Summary of displayImgOxadata
     * @param mixed $path
     * @return string
     */
    public function displayImgOxadata($path)
    {
        /**
         * TODO ModSecurity
         */
        $fileContent = $path ? file_get_contents($path): '';
        if ($fileContent) {
            $image_path = base64_encode($fileContent);
            return '<img src="data:image/x-icon;base64,'.$image_path.'" height="auto" width="50%">';
        }

        return '';
    }

    /**
     * Summary of str_replace
     * phpcs:disable
     * @param mixed $char
     * @param mixed $replace
     * @param mixed $string
     * @return string[]|string
     */
    public function str_replace($char, $replace, $string)
    {
        // phpcs:enable
        return str_replace($char, $replace, $string);
    }
    
    /**
     * Summary of tradDatePickers
     * @return string
     */
    public function tradDatePickers()
    {

        return "	  $.datepicker.regional['fr'] = {
            showButtonPanel: true,
            clearText: '".$this->trad->t('Effacer')."', 
            clearStatus: '',
            closeText: '".$this->trad->t('Fermer')."', 
            closeStatus: '".$this->trad->t('Fermer sans modifier')."',
            prevText: '&lt;".$this->trad->t('Préc')."', 
            prevStatus: '".$this->trad->t('Voir le mois précédent')."',
            nextText: '".$this->trad->t('Suiv')."&gt;', 
            nextStatus: '".$this->trad->t('Voir le mois suivant')."',
            currentText: '".str_replace("'", '’', $this->trad->t('Aujourd\'hui'))."', 
            currentStatus: '".$this->trad->t('Voir le mois courant')."',
            monthNames: ['".$this->trad->t("Janvier")."','". $this->trad->t('Février')."','". $this->trad->t('Mars') ."','". $this->trad->t('Avril')."','".$this->trad->t('Mai')."','".$this->trad->t('Juin')."','".$this->trad->t('Juillet')."','".$this->trad->t('Août')."','".$this->trad->t('Septembre')."','".$this->trad->t('Octobre')."','".$this->trad->t('Novembre')."','".$this->trad->t('Décembre')."'],

            monthNamesShort: ['".$this->trad->t("Jan")."','". $this->trad->t('Fév')."','". $this->trad->t('Mar')."','". $this->trad->t('Avr')."','".$this->trad->t('Mai')."','".$this->trad->t('Jun')."','".$this->trad->t('Jul')."','".$this->trad->t('Aoû')."','".$this->trad->t('Sep')."','".$this->trad->t('Oct')."','".$this->trad->t('Nov')."','".$this->trad->t('Déc')."'],
            monthStatus: '".$this->trad->t('Voir un autre mois')."', 
            yearStatus: '".$this->trad->t('Voir une autre année')."',
            weekHeader: 'Sm', 
            weekStatus: '',
            dayNames: ['".$this->trad->t('Dimanche')."','".$this->trad->t("Lundi")."','". $this->trad->t('Mardi')."','". $this->trad->t('Mercredi') ."','". $this->trad->t('Jeudi')."','".$this->trad->t('Vendredi')."','".$this->trad->t('Samedi')."'],
            dayNamesMin: ['".$this->trad->t('Di')."','".$this->trad->t("Lu")."','".$this->trad->t('Ma')."','". $this->trad->t('Me') ."','".$this->trad->t('Je')."','".$this->trad->t('Ve')."','".$this->trad->t('Sa')."'],
            dayNamesShort:  ['".$this->trad->t('Dim.')."','".$this->trad->t("Lun.")."','".$this->trad->t('Mar.')."','". $this->trad->t('Mer.') ."','". $this->trad->t('Jeu.')."','".$this->trad->t('Ven.')."','".$this->trad->t('Sam.')."'],
            dayStatus: '".$this->trad->t('Utiliser DD comme premier jour de la semaine')."', 
            dateStatus: '".$this->trad->t('Choisir le DD, MM d')."',
            dateFormat: 'dd/mm/yy', 
            firstDay: 1, 
            initStatus: '".$this->trad->t('Choisir la date')."', isRTL: false};";
    }
    
    /**
     * Summary of str_pad
     * phpcs:disable
     * @param mixed $value
     * @param mixed $length
     * @param mixed $valeurComplement
     * @param mixed $orientation
     * @return string
     */
    public function str_pad($value, $length = 9, $valeurComplement = '', $orientation = 'right')
    {
        // phpcs:disable
        $orientation = match ($orientation) {
            'right', 'STR_PAD_RIGHT' => STR_PAD_RIGHT,
            'left', 'STR_PAD_LEFT' => STR_PAD_LEFT,
            'both', 'STR_PAD_BOTH' => STR_PAD_BOTH,
            default => STR_PAD_RIGHT,
        };
        return  str_pad((string) $value, $length, $valeurComplement, $orientation);
    }

    /**
     * Summary of convertIDToInput
     * @param mixed $input
     * @return string[]|string
     */
    public function convertIDToInput($input)
    {
        
        $inputFinal = str_replace('.', Constante::replacePoint, $input);
        
        return str_replace(' ', Constante::replaceEspace, $inputFinal);
    }
    
    /**
     *
     * @param string $zoneHover // div qui activera le hover
     * @param string $icone     // icone a afficher °
     * @param string $onclick   // fonction a executer (redirection, open popup...) sur click
     * @param string $style   
     * @param string $tooltips   
     * @param string $tooltipV2   
     * @param bool $onhover   // fonction a executer (redirection, open popup...) sur click
     *
     * Liste °:
     * menu_contextuel_v2
     * ajouter_v2
     * modifier_v2
     * Supprimer_v2
     * visualiser_v2
     *
     * @return string
     */
    // phpcs:disable
    public function hover_action($zoneHover, $icone, $onclick, $style = '', $tooltips = '', $tooltipV2 = '', $onhover = false)
    {
        // phpcs:enable
        random_int(0, 999999999);
        return "

            <span class='$icone  ".($tooltipV2? 'white-tooltip':'')."' data-container-contextuel='$zoneHover' style='".($zoneHover? 'opacity: 0':'opacity: 1')."; $style' " . ($onhover ? "onmouseover" : "onclick") . "=\"".htmlspecialchars($onclick)."\" title=\"$tooltips\">
                <span id='addChamps' class='infos_tooltip left'>
                <span class='tooltip-arrow'></span>$tooltipV2</span>
            </span>";
    }

    public function generateDataTooltips(string $position, string $texte, bool $container = false) : string
    {
        return '  '.($container? 'data-container="#div_tooltip"':'').' data-toggle="tooltip" data-placement="'.$position.'" data-html="true" data-original-title="'.$texte.'"';
    }

    public function buttonUpDown() : string
    {
        return '            			
            <div class="divDeplacement" style="display:none;">
                <button onClick="$(\'html,body\').animate({ scrollTop: 0},\'slow\');" type="button" class="button principal buttonDeplacer btn_double_top"></button>
                <button onClick="$(\'html,body\').animate({ scrollTop: $(document).scrollTop()-2050},\'slow\');" type="button" class="button principal buttonDeplacer btn_top"></button>
                <button onClick="$(\'html,body\').animate({ scrollTop: $(document).scrollTop()+2050},\'slow\');" type="button" class="button principal buttonDeplacer btn_bottom"></button>
                <button onClick="$(\'html,body\').animate({ scrollTop: $(document).height()},\'slow\');" type="button" class="button principal buttonDeplacer btn_double_bottom"></button>
            </div>	
            <script>
                $( ".divDeplacement" ).draggable();
                $( document ).ready(function() {
                    var sizeScreen = $( document ).height();

                    if (sizeScreen > 3000) {
                        $(\'.divDeplacement\').show();
                    }
                });
            </script>';
    }

    /**
     * attr
     * Turn a map into a lsit of html attribute (string)
     *
     * @param  array<mixed> $attr
     */
    public function attr($attr):string
    {
        return XmlHelper::attr($attr);
    }

    /**
     * Shorthand for "| json_encode | raw"
     *
     * @param  mixed $data
     */
    public function json($data) :string
    {
        $s = json_encode($data);
        if ($s === false) {
            throw new Exception(json_last_error_msg());
        }
        return $s;
    }
    
    // phpcs:ignore
    static int $uniqId = 0;
    public function uid() :int
    {
        return self::$uniqId++;
    }
    
    /**
     * @param array<mixed>|null|object $entity
     */
    public static function entityLabel($entity, string $codeField, string $labelField) :string
    {
        if (empty($entity)) {
            return '';
        }
        if (is_object($entity)) {
            $code = $entity->{ $codeField } ?? '';
            $value = $entity->{ $labelField } ?? '';
        } else {
            $code = $entity[ $codeField ] ?? '';
            $value = $entity[ $labelField ] ?? '';
        }
        if (empty($code)) {
            return '';
        }
        return $value . ' ('. $code . ')';
    }

    /**
     * Je ne saisie pas l'utilité de cette fonction ? on a déjà le convertLibelle qui retourne libelle (code) pourquoi redevelopper une fonction ?
     * @param array<mixed>|null|object $entity
     */
    public function hastutilLabel($entity) :string
    {
        return static::entityLabel($entity, 'CODE', 'NOM');
    }
    
    public function hasRight(string $droit) :bool
    {
        return Roles::droitsUtilisateur($this->pdo, $droit) == 1;
    }

    /**
     * @return string
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function connexionImage()
    {

        if ($lienBG = Sql::paravrac($this->pdo, 'OXAPROC', 'PAGECNX_URLIMAGE')) {
            if (Sql::paravrac($this->pdo, 'OXAPROC', 'TYPE_HEADER_URL_RELATIVE')=='O') {
                $link = (new ParseBase1())->getPath()->oxadataWeb.$lienBG;
            } else {
                $link = $lienBG;
            }
            $image = '<img src="'.Fichier::updateImageFromSource($link).'"/>';
        } else {
            $image = '<img src="'.$this->contenaire->get('base')."V2/IconeV2/Misc/Page_Connexion_Principale.png".'"/>';
        }

        return '<div class="colonneImage">
    			     '.$image.'
                 </div>';
    }
    
    public function updateImageFromSource(string $link) : string
    {
        return Fichier::updateImageFromSource($link);
    }

    public function jsTemplate(string $content, string $id) :string
    {
        if (empty($id)) {
            throw new Exception('Id required');
        }

        return "<script>
            if(typeof window.template !== 'object') window.template = {};
            window.template[". json_encode($id).'] = '.json_encode($content).'</script>';
    }

    public function excelColLabel(int $index) :string
    {
        return Coordinate::stringFromColumnIndex($index);
    }
    public function importFormatLabel(string $format): string
    {
        return match ($format) {
            'A' => $this->trad->t('Alphanumérique'),
            'N' => $this->trad->t('Numérique'),
            'D' => $this->trad->t('Date'),
            'L' => $this->trad->t('Booléen'),
            'M' => $this->trad->t('Texte'),
            default => $format,
        };
    }

    /**
     * oxalinkUserStatut
     *
     * @param  string $oxalinkid
     */
    public function oxalinkUserStatut(?string $oxalinkid):string
    {
        if (!$oxalinkid) {
            return $this->trad->t('Non');
        }

        $tableauStatut = [
            'C' => $this->trad->t('En création'),
            'A' => $this->trad->t('Actif'),
            'B' => $this->trad->t('Bloqué'),
            'F' => $this->trad->t('Fermé'),
            'V' => $this->trad->t('En attente de validation'),
        ];

        $statut = $this->oxalinkUser->find($oxalinkid);

        if (!$statut) {
            return $this->trad->t('Non');
        }

        return $tableauStatut[$statut['STATUT']];
    }
    
    public function slug(string $string) :string
    {
        return $this->contenaire
            ->get(SluggerInterface::class)
            ->slug($string);
    }
    
    /**
     * @param stdClass $obj
     * @return stdClass
     */
    public function setObj(object $obj, string $field, mixed $value): object
    {
        $obj->{$field} = $value;
        return $obj;
    }

    /**
     * @param array<mixed> $array
     * @return array<mixed>
     */
    public function values(array $array): array
    {
        return array_values($array);
    }

    /**
     * @param class-string<ILabelized&\BackedEnum> $enumClass
     * @return array<string|int,string>
     */
    public function enumToChoice(string $enumClass): array
    {
        return EnumHelper::enumToChoice($enumClass, $this->trad);
    }

    /**
     * @param mixed $value
     * @param class-string<\BackedEnum>|null $enumClass
     */
    public function enumToLabel($value, ?string $enumClass = null): string
    {
        // Case : fallback -> try to convert value into enum
        if (! ($value instanceof ILabelized) && !empty($enumClass)) {
            $value = $enumClass::tryFrom($value);
            if ($value == null) {
                return '';
            }
        }

        if (! ($value instanceof ILabelized)) {
            throw new \Exception('Enum is not labelizable');
        }

        /** @var $value mixed */
        return $value->getLabel($this->trad);
    }

    public function isSuperAdmin() :bool
    {
        return $_SESSION["Session_Login"] == 'OXAUSER' || Roles::GetALL();
    }

    public function licenceOxa() :LicenceOxalys
    {
        return new LicenceOxalys($this->pdo);
    }
}
