<?php

namespace Entity\Model;

use Helpers\Cache\Cache;
use Helpers\Outil\Roles;
use Helpers\Outil\Sql;
use Helpers\QueryBuilder\QueryBuilder;
use Skeleton\Router;
use Helpers\Session\ParseBase1;

class Paravrac extends QueryBuilder
{
    protected string $table = 'PARAVRAC with (nolock)';
    protected string $primaryKey = '';
    protected array $primKey = [
        'TYPEPARV',
        'CODEPARV',
    ];

    /**
     * find
     *
     * @param  mixed $id
     * @param  array<mixed> $selectTables
     * @param  array<mixed> $post
     * @param  mixed $option
     * @return array<mixed>
     */
    public function find($id, array $selectTables = [], $post = [], $option = false)
    {
        if (is_string($id)) {
            $arrayTmp = $id;
        } else {
            asort($id);
            $arrayTmp = implode('_', $id);
        }

        $winCacheResult = \Helpers\Cache\Cache::surchargeGetUcache('PARAM'.(new ParseBase1())->getBroker());
      
        if (isset($winCacheResult[$arrayTmp])) {
            return $winCacheResult[$arrayTmp];
        } else {
            $returnReqArray = parent::find($id, $selectTables, $post, $option);
            $winCacheResult[$arrayTmp] = $returnReqArray;
            \Helpers\Cache\Cache::surchargeSetUcache('PARAM'.(new ParseBase1())->getBroker(), $winCacheResult);
            return $returnReqArray;
        }
    }
    
    /**
     * findAll
     *
     * @param  mixed $id
     * @param  array<mixed> $selectTables
     * @param  array<mixed> $post
     * @param  mixed $entity
     * @param  mixed $exact
     * @return mixed
     */
    public function findAll($id, array $selectTables = [], $post = [], $entity = null, $exact = false)
    {
        if (is_string($id)) {
            $arrayTmp = $id;
        } else {
            asort($id);
            $arrayTmp = implode('_', $id);
        }

        $winCacheResult = \Helpers\Cache\Cache::surchargeGetUcache('PARAM'.(new ParseBase1())->getBroker());

        if (isset($winCacheResult[$arrayTmp])) {
            return $winCacheResult[$arrayTmp];
        } else {
            $returnReqArray = parent::findAll($id, $selectTables, $post, $entity, $exact);
            $winCacheResult[$arrayTmp] = $returnReqArray;
            \Helpers\Cache\Cache::surchargeSetUcache('PARAM'.(new ParseBase1())->getBroker(), $winCacheResult);
            return $returnReqArray;
        }
    }
    /**
     * update
     *
     * @param  array<mixed> $valueUpdate
     * @param  mixed $id
     * @return bool
     */
    public function update(array $valueUpdate, $id) : bool
    {
        \Helpers\Cache\Cache::surchargeDeleteUcache('PARAM'.(new ParseBase1())->getBroker());
        
        return parent::update($valueUpdate, $id);
    }

    /**
     * @param array<mixed> $entity
     */
    public function merge(array $entity) :void
    {
        \Helpers\Cache\Cache::surchargeDeleteUcache('PARAM'.(new ParseBase1())->getBroker());
        
        parent::merge($entity);
    }


    public function set(string $type, string $key, string $value) :void
    {

        // TODO : update instead of deleting
        Cache::surchargeDeleteUcache('PARAM'.(new ParseBase1())->getBroker());
        
        $req = "MERGE PARAVRAC as Target
            USING (SELECT 
                    :type as TYPEPARV,
                    :key as CODEPARV,
                    :value as LIB01PARV
                ) as Entity
                ON Target.TYPEPARV = Entity.TYPEPARV
                AND Target.CODEPARV = Entity.CODEPARV
            WHEN NOT MATCHED
                THEN INSERT(TYPEPARV, CODEPARV, LIB01PARV) 
                    VALUES(Entity.TYPEPARV, Entity.CODEPARV, Entity.LIB01PARV)
            WHEN MATCHED 
                THEN UPDATE SET Target.LIB01PARV = Entity.LIB01PARV;";
                
        $stmt = $this->pdo->prepare($req);
        $b = $stmt->execute([
            'type' => $type,
            'key' => $key,
            'value' => $value,
        ]);
        if ($b === false) {
            throw new \Exception('!!!');
        }
    }

//_____________________________________________________________________________
// Shorthand

    /**
     * @return array<string>
     */
    public function getMap(string $key)
    {
        return array_column(
            (new Paravrac($this->pdo))->findAll([
                'TYPEPARV' => $key,
            ]),
            'LIB01PARV',
            'CODEPARV'
        );
    }

    public function getValue(string $category, string $key) :?string
    {
        return $this->find([
            'TYPEPARV' => $category,
            'CODEPARV' => $key,
        ])['LIB01PARV'] ?? null;
    }

    public function getFlag(string $category, string $key) :bool
    {
        return Sql::toBool($this->getValue($category, $key));
    }
    
    public function getGeneric(string $key) :?string
    {
        return $this->getValue('PARAMGEN', $key);
    }

    public function getForSig(string $key) :?string
    {
        return $this->getValue('PARAMSIG', $key);
    }
    public function getFlagForSig(string $key) :?bool
    {
        return $this->getFlag('PARAMSIG', $key);
    }

    public function getForSoc(string $socId, string $key) :?string
    {
        return $this->getValue('PARAMSOC', $key . $socId);
    }

    public function getForSte(string $key) :?string
    {
        return $this->getValue('PARAMSTE', $key);
    }

    public function getDemo(string $product, string $val) :?string
    {
        if ($val == '') {
            $key = implode('_', [$product, 'Ok']);
        } else {
            $key = implode('_', [$product, $val, 'Ok']);
        }
        return $this->getValue('CLT_DEMO_L', $key);
    }

    /**
     * /!\
     * For phpstan 1.10.12:
     * phpstan-type ConfigItem object{isActive:bool,isBlankAllowed:bool,isSecto:bool}
     * return object{
     *     SectImp:ConfigItem,
     *     CodeCompl:ConfigItem,
     *     SousCompteAna:ConfigItem
     * }
     * @return \stdClass
     */
    public function getCommandeAxeImpConfig(?string $socId = null) :object
    {
        $socId = $socId ?? '';
        $isActive = $this->getImputActiveFlag('CA');
        return (object) [
            'SectImp' => (object) [
                'isBlankAllowed' => $this->isAxeImpBlankAllowed($socId, 'SI'),
                'isActive' => $isActive->SI,
                'isSecto' => Sql::toBool($this->getForSte('SISECTOR')),
            ],
            'CodeCompl' => (object) [
                'isBlankAllowed' => $this->isAxeImpBlankAllowed($socId, 'CDC'),
                'isActive' => $isActive->CDC,
                'isSecto' => Sql::toBool($this->getForSte('CDCSECTO')),
            ],
            'SousCompteAna' => (object) [
                'isBlankAllowed' => $this->isAxeImpBlankAllowed($socId, 'SCA'),
                'isActive' => $isActive->SCA,
                'isSecto' => Sql::toBool($this->getForSte('SCASECTO')),
            ],
        ];
    }

    /**
     * /!\
     * For phpstan 1.10.12:
     * phpstan-type ConfigItem object{isActive:bool,isBlankAllowed:bool,isSecto:bool}
     * return object{
     *     SectImp:ConfigItem,
     *     CodeCompl:ConfigItem,
     *     SousCompteAna:ConfigItem
     * }
     * @return \stdClass
     */
    public function getDemandeAxeImpConfig(?string $socId = null) :object
    {
        $socId = $socId ?? '';
        $isActive = $this->getImputActiveFlag('DA');
        
        return (object) [
            'SectImp' => (object) [
                'isBlankAllowed' => $this->isAxeImpBlankAllowedDA($socId, 'SI'),
                'isActive' => $isActive->SI,
                'isSecto' => Sql::toBool($this->getForSte('SISECTOR')),
            ],
            'CodeCompl' => (object) [
                'isBlankAllowed' => $this->isAxeImpBlankAllowedDA($socId, 'CDC'),
                'isActive' => $isActive->CDC,
                'isSecto' => Sql::toBool($this->getForSte('CDCSECTO')),
            ],
            'SousCompteAna' => (object) [
                'isBlankAllowed' => $this->isAxeImpBlankAllowedDA($socId, 'SCA'),
                'isActive' => $isActive->SCA,
                'isSecto' => Sql::toBool($this->getForSte('SCASECTO')),
            ],
        ];
    }

    /**
     * @param string $doc 'CA' | 'DA'
     * @return \stdClass
     */
    private function getImputActiveFlag(string $doc)
    {
         // 0: SI, 1: CDC, 2: SCA
        $a = array_map(
            function (string $s) {
                return ! Sql::toBool($s);
            },
            // INVISIBLE_SUR_CA_SI_CDC_SCA is a concatenation of 3 flag 'O'/'N' with '|' as glue
            explode('|', $this->getGeneric(
                'INVISIBLE_SUR_'.$doc.'_SI_CDC_SCA'
            ))
        );
        return (object) [ 'SI' => $a[0]??true, 'CDC' => $a[1]??true, 'SCA' => $a[2]??true ];
    }

    public function getTypePreaffectation() :string
    {
        return $this->getGeneric('PREAF_MAIL') ?? '';
    }

    public function isSignBapActive() :bool
    {
        return Sql::toBool($this->getValue('PARAMINIFA', 'SIGN_BAP'));
    }


    public function hasSignPassword() :bool
    {
        return ! $this->getFlag('PARAMGEN', 'PAS_DE_MDP_SIG');
    }

    /**
     * @return int float maybe ?
     */
    public function getFileSizeMax()
    {
        $max = intval($this->getGeneric('PJ_TAILLEMAX'));

        if ($max == 0) {
            return 5000000;
        }
        return $max * 1000000;
    }

    public function getPaysInstall() :string
    {
        return $this->getGeneric('PAYSINSTAL') ?? 'FR';
    }

    public function getParamNewAccount(Router $router):string
    {
        $param = sql::paravrac($this->pdo, 'OXAPROC', 'LOGIN_DDE_COMPTE_URL');
        
        $r = sql::paravrac($this->pdo, 'OXAPROC', 'LOGIN_MODE_FONCTION');
        if ('P' == $r || ('S' == $r) && '' == trim($param)) {
            $param = $router->generateUri('Index.CreationCompte.cordonne');
        } elseif ('S' == $r) {
        } elseif ('I' == $r) {
            $param = 'I';
        }

        return $param;
    }

    public function getUrlAide():string
    {
        $urlAide = '';
        $TypeAide = sql::paravrac($this->pdo, 'OXAPROC', 'AIDE_TYPE');
        if ($TypeAide=='C') {
            $AideClientURL = sql::paravrac($this->pdo, 'OXAPROC', 'AIDE_URL_EXT');
            if ($AideClientURL) {
                $urlAide = $AideClientURL;
            }
        }
        
        return $urlAide;
    }


    public function isEnvProd() :bool
    {
        return $this->getGeneric('TypeEnvironnement') == 'P';
    }

//_____________________________________________________________________________
// Sub-routine

    /**
     * Ce paramtre ne s'applique qu'aux ligne de commande
     * @param string $codeAxe enum 'SI'/'CDC'/'SCA'
     */
    private function isAxeImpBlankAllowed(string $socId, string $codeAxe) :bool
    {
        $paramKey = 'IMP_BO_' . $codeAxe. ($codeAxe == 'SI' ? '_' : '') . '_CDE';
        return Sql::toBool(
            $this->getForSoc($socId, $paramKey)
                ?? $this->getGeneric($paramKey)
                ?? false
        );
    }

    /**
     * Ce paramtre ne s'applique qu'aux ligne de commande
     * @param string $codeAxe enum 'SI'/'CDC'/'SCA'
     */
    private function isAxeImpBlankAllowedDA(string $socId, string $codeAxe) :bool
    {
        $paramKey = 'IMP_BO_' . $codeAxe. ($codeAxe == 'SI' ? '_' : '') . '_DA_';
        return Sql::toBool(
            $this->getForSoc($socId, $paramKey)
                ?? $this->getGeneric($paramKey)
                ?? false
        );
    }

    /**
     * @return \stdClass{SI:bool,CG:bool,CDC:bool,SCA:bool}
     */
    public function getVisibiliteLigne():object
    {
        return (object) [
            'SI'    => Roles::droit($this->pdo, 'SILIGDA', 'INTRAVISIB'),
            'CG'    => Roles::droit($this->pdo, 'SILIGDA', 'INTRAVISIB'),
            'CDC'   => Roles::droit($this->pdo, 'CDCLIGDA', 'INTRAVISIB'),
            'SCA'   => Roles::droit($this->pdo, 'SCALIGDA', 'INTRAVISIB'),
        ];
    }

    /**
     * @return \stdClass{SI:bool,CG:bool,CDC:bool,SCA:bool}
     */
    public function getVisibiliteEntete(): object
    {
        return (object) [
            'SI'  => Roles::droit($this->pdo, 'SIENTDA', 'INTRAVISIB'),
            'CG'  => Roles::droit($this->pdo, 'SIENTDA', 'INTRAVISIB'),
            'CDC' => Roles::droit($this->pdo, 'CDCENTDA', 'INTRAVISIB'),
            'SCA' => Roles::droit($this->pdo, 'SCAENTDA', 'INTRAVISIB'),
        ];
    }
}
