<?php

namespace Helpers\CaracCompl;

use Entity\Model\Articles;
use Entity\Model\CCLDoc;
use Entity\Model\SousFamille;
use Helpers\Outil\Sql;
use Modules\Reception\Repository\CreationBR;
use Modules\Panier\Repository\PanierLigne;
use Throwable;

class CaracCompl
{
    /**
     * @var \PDO $pdo
     */
    private $pdo;

    /**
     * @var array <mixed> $searchCC
     */
    private $searchCC;

    /**
     * @var array <mixed> $listeCC
     */
    private $listeCC;

    private CreationBR $creationBr;



    public function __construct(\PDO $pdo, string $article = '', string $ssFamille = '', string $famille = '')
    {
        $this->pdo = $pdo;

        if ($article && !$ssFamille) {
            $articleEntity = new Articles($this->pdo);
            $infoArt = $articleEntity->find($article);

            $ssFamille = $infoArt['SSFAM'] ?? '';
        }

        if ($ssFamille && !$famille) {
            $ssFamilleEntity = new SousFamille($this->pdo);
            $infoSSFamille = $ssFamilleEntity->find($ssFamille);

            $famille = $infoSSFamille['CODE_FAM'] ?? '';
        }
        $this->searchCC = [
            'ART' => $article,
            'SFA' => $ssFamille,
            'FAM' => $famille,
        ];

        $this->creationBr   = new CreationBR($this->pdo);
    }

    /**
     * @return array <mixed>
     */
    public function getListeCCInit(bool $onlyCalc = false)
    {
        $listeCC = [];
        foreach ($this->searchCC as $key => $CC) {
            $listeCC = $this->getCCInit($key, $CC, $onlyCalc);

            if ($listeCC) {
                break;
            }
        }

        return $listeCC;
    }

    /**
     * @return array <mixed>
     */
    public function getCCInit(string $tableId, string $recordId, bool $onlyCalc = false)
    {
        $req = "SELECT '1' CC, CC_REF.CC_TABLE_ID, CC_REF.CC_RECORD_ID, CC_REF.CC_ID, CC_REF.CC_DISPLAY_NUM, CC_REF.CC_IS_TITLE, CC_REF.CC_NAME NOM, CC_REF.CC_TYPE, CC_REF.CC_UPD_ONLY_RECEIPT, CC_REF.CC_INVIS_RECEIPT, CC_REF.CC_REQUIRED, CC_REF.CC_PRINTED_OF, CC_REF.CC_CALC, CC_REF.CC_CALC_EXP, 
                CS_TYPES.FORMAT, CS_TYPES.ALP_LONG, CS_TYPES.PE_LONG, CS_TYPES.PD_LONG, CS_TYPES.NEG_AUTO, CS_TYPES.CONTROLE, CS_TYPES.CTRL_STD, CS_TYPES.TBLSECTO
                FROM CC_REF with (nolock) 
                LEFT JOIN CS_TYPES with (nolock) on CS_TYPES.TYPE=CC_REF.CC_TYPE
                WHERE CC_REF.CC_TABLE_ID =:tableId AND CC_REF.CC_RECORD_ID = :recordId";

        if ($onlyCalc) {
            $req .= " and CC_REF.CC_CALC = 0x54 ";
        }

        $req .= " ORDER BY CC_REF.CC_DISPLAY_NUM";

        $arrayReq = [
            ':tableId' => $tableId,
            ':recordId' => $recordId,
        ];

        $requete = $this->pdo->prepare($req);
        $requete->execute($arrayReq);
        
        $return = $requete->fetchAll($this->pdo::FETCH_ASSOC);
        if ($return) {
            return $return;
        } else {
            return [];
        }
    }

    public function ccExist() : bool
    {
        $clause = '';
        $arrayReq = [];

        $i = 1;
        foreach ($this->searchCC as $k => $cc) {
            if ($clause) {
                $clause .= " OR ";
            }

            $clause .= "(CC_REF.CC_TABLE_ID = :idTable$i AND CC_REF.CC_RECORD_ID = :idRecord$i)";

            $arrayReq[":idTable$i"] = $k;
            $arrayReq[":idRecord$i"] = $cc;
            $i++;
        }

        if (!$clause) {
            return false;
        }

        $req = "SELECT COUNT(*) countCC
                FROM CC_REF with (nolock) 
                LEFT JOIN CS_TYPES with (nolock) on CS_TYPES.TYPE=CC_REF.CC_TYPE
                WHERE $clause
        ";


        $requete = $this->pdo->prepare($req);
        $requete->execute($arrayReq);

        $return = $requete->fetch($this->pdo::FETCH_ASSOC);
        
        if ($return) {
            return $return['countCC']!='0'? true:false;
        } else {
            return false;
        }
    }

    /**
     * @param array<mixed> $listeArt
     * @param array<mixed> $listeSfm
     * @param array<mixed> $listeFam
     */
    public function searchMultiCC($listeArt = [], $listeSfm = [], $listeFam = []) : void
    {

        $liste = [
            'ART' => $listeArt,
            'SFA' => $listeSfm,
            'FAM' => $listeFam
        ];

        $clause = '';
        $arrayReq = [];

        $i = 1;
        foreach ($liste as $k => $typeCC) {
            foreach ($typeCC as $cc) {
                if ($clause) {
                    $clause .= " OR ";
                }
    
                $clause .= "(CC_REF.CC_TABLE_ID = :idTable$i AND CC_REF.CC_RECORD_ID = :idRecord$i )";
                
                $arrayReq[":idTable$i"] = $k;
                $arrayReq[":idRecord$i"] = $cc;
                $i++;
            }
        }

        if ($clause) {
            $req = "select CC_TABLE_ID,CC_RECORD_ID, COUNT(*) count 
                    from CC_REF with(nolock)
                    where $clause
                    group by CC_TABLE_ID, CC_RECORD_ID";

            $requete = $this->pdo->prepare($req);
            $requete->execute($arrayReq);
            $listeCCTmp = $requete->fetchAll($this->pdo::FETCH_ASSOC);
            
            $listeCC = [];
            if ($listeCCTmp) {
                foreach ($listeCCTmp as $ccTmp) {
                    $listeCC[$ccTmp['CC_TABLE_ID']][$ccTmp['CC_RECORD_ID']] = $ccTmp['count'];
                }
            }

            $this->listeCC = $listeCC;
        }
    }

    public function multiExistCC(?string $codeArt = null, ?string $codeSfam = null, ?string $codeFam = null) : bool
    {
        if (isset($this->listeCC['ART'][$codeArt])
            || isset($this->listeCC['SFA'][$codeSfam])
            || isset($this->listeCC['FAM'][$codeFam])
        ) {
            return true;
        }

        return false;
    }

    /**
     * @return array <mixed>
     */
    public function getCCDocc(string $docType, string $docNum, string $idLigneDoc, string $idUserPanier, bool $onlyCalc = false, string $amd = '')
    {

        if ($docType == 'CDE') {
            $docType = 'CA';
        }

        $req = "SELECT '1' CC, CC_LDOC.DOC_TYPE, CC_LDOC.DOC_NUM, CC_LDOC.DOC_LINE_NUM, CC_LDOC.ID_USER_BASKET, 
                CC_LDOC.DOC_AMD, CC_LDOC.CC_ID, CC_LDOC.CC_FREE, CC_LDOC.CC_DISPLAY_NUM, 
                CC_LDOC.CC_IS_TITLE, CC_LDOC.CC_NAME NOM, CC_LDOC.CC_TYPE, CC_LDOC.CC_UPD_ONLY_RECEIPT, CC_LDOC.CC_INVIS_RECEIPT, 
                CC_LDOC.CC_REQUIRED, CC_LDOC.CC_PRINTED_OF, CC_LDOC.CC_CALC, CC_LDOC.CC_CALC_EXP, CC_LDOC.CC_VALUE VALEUR, 
                CS_TYPES.FORMAT, CS_TYPES.ALP_LONG, CS_TYPES.PE_LONG, CS_TYPES.PD_LONG, CS_TYPES.NEG_AUTO, CS_TYPES.CONTROLE, CS_TYPES.CTRL_STD, CS_TYPES.TBLSECTO
                FROM CC_LDOC with (nolock) 
                LEFT JOIN CS_TYPES with (nolock) on CS_TYPES.TYPE=CC_LDOC.CC_TYPE
                WHERE CC_LDOC.DOC_TYPE = :docType AND CC_LDOC.DOC_NUM =:docNum AND CC_LDOC.DOC_LINE_NUM =:idLigneDoc and CC_LDOC.ID_USER_BASKET = :idUserPanier";

        $arrayReq = [
            ':docType' => $docType,
            ':docNum' => $docNum,
            ':idLigneDoc' => $idLigneDoc,
            ':idUserPanier' => $idUserPanier,
        ];

        if ($onlyCalc) {
            $req .= " and CC_LDOC.CC_CALC = 0x54 ";
        }

        if ($amd || $docType == 'HCA') {
            $req .= " and DOC_AMD = :amd";
            $arrayReq[':amd'] = $amd === '0' ? '' : $amd;
        }

        $req .= " ORDER BY CC_LDOC.CC_DISPLAY_NUM";



        $requete = $this->pdo->prepare($req);
        $requete->execute($arrayReq);
        
        $return = $requete->fetchAll($this->pdo::FETCH_ASSOC);
        if ($return) {
            return $return;
        } else {
            return [];
        }
    }


    public function deleteCC(string $typeDoc, string $idDoc, string $idLigne, string $idPanier = '0', string $ccID = ''): void
    {
        $ccLDoc = new CCLDoc($this->pdo);

        $arrayDelete = [
            'DOC_TYPE'          => $typeDoc,
            'DOC_NUM'           => $idDoc,
            'DOC_LINE_NUM'      => $idLigne,
            'ID_USER_BASKET'    => $idPanier,
        ];

        if ($ccID) {
            $arrayDelete['CC_ID'] = $ccID;
        }
        
        $ccLDoc->delete($arrayDelete);
    }

    /**
     * @param array<mixed> $post
     */
    public function insertCC(string $newDoc, string $newNum, string $newLigne, string $newPanier = '0', $post = []): void
    {
        $listeCC = $this->getListeCCInit();

        $ccLDoc = new CCLDoc($this->pdo);

        foreach ($listeCC as $cc) {
            if ($cc['FORMAT'] == 'L') {
                $value = ($post['CC_' . $cc['CC_ID']] ?? false) ? 'O' : 'N';
            } else {
                $value = $post['CC_' . $cc['CC_ID']] ?? '';
            }

            $ccLDoc->insert([
                'DOC_TYPE'                          => $newDoc,
                'DOC_NUM'                           => $newNum,
                'DOC_LINE_NUM'                      => $newLigne,
                'ID_USER_BASKET'                    => $newPanier,
                'CC_ID'                             => $cc['CC_ID'],
                'numerique_CC_FREE'                 => '0x46',
                'CC_DISPLAY_NUM'                    => $cc['CC_DISPLAY_NUM'],
                'numerique_CC_IS_TITLE'             => sql::setBoolSql(Sql::boolSql($cc['CC_IS_TITLE'])),
                'CC_NAME'                           => $cc['NOM'],

                'CC_TYPE'                           => $cc['CC_TYPE'],
                'numerique_CC_UPD_ONLY_RECEIPT'     => sql::setBoolSql(Sql::boolSql($cc['CC_UPD_ONLY_RECEIPT'])),
                'numerique_CC_INVIS_RECEIPT'        => sql::setBoolSql(Sql::boolSql($cc['CC_INVIS_RECEIPT'])),
                'numerique_CC_REQUIRED'             => sql::setBoolSql(Sql::boolSql($cc['CC_REQUIRED'])),
                'numerique_CC_PRINTED_OF'           => sql::setBoolSql(Sql::boolSql($cc['CC_PRINTED_OF'])),
                'numerique_CC_CALC'                 => sql::setBoolSql(Sql::boolSql($cc['CC_CALC'])),
                'CC_CALC_EXP'                       => $cc['CC_CALC_EXP'],
                'CC_VALUE'                          => $value,
            ]);
        }
    }

    /**
     * duplicateCC
     *
     * @param  mixed $newDoc
     * @param  mixed $newNum
     * @param  mixed $newLigne
     * @param  mixed $newUserPanier //id user panier
     * @param  mixed $oldDoc
     * @param  mixed $oldNuw
     * @param  mixed $oldLigne
     * @param  mixed $oldUserPanier //id user panier
     * @param  mixed $amd
     * @return void
     */
    public function duplicateCC($newDoc, $newNum, $newLigne, $newUserPanier, $oldDoc, $oldNuw, $oldLigne, $oldUserPanier = '0', $amd = ''): void
    {
        $listeCC = $this->getCCDocc($oldDoc, $oldNuw, $oldLigne, $oldUserPanier);

        $ccLDoc = new CCLDoc($this->pdo);

        foreach ($listeCC as $cc) {
            $ccLDoc->insert([
                'DOC_TYPE'                          => $newDoc,
                'DOC_NUM'                           => $newNum,
                'DOC_LINE_NUM'                      => $newLigne,
                'ID_USER_BASKET'                    => $newUserPanier,
                'CC_ID'                             => $cc['CC_ID'],
                'numerique_CC_FREE'                 => '0x46',
                'CC_DISPLAY_NUM'                    => $cc['CC_DISPLAY_NUM'],
                'numerique_CC_IS_TITLE'             => sql::setBoolSql(Sql::boolSql($cc['CC_IS_TITLE'])),
                'CC_NAME'                           => $cc['NOM'],

                'CC_TYPE'                           => $cc['CC_TYPE'],
                'numerique_CC_UPD_ONLY_RECEIPT'     => sql::setBoolSql(Sql::boolSql($cc['CC_UPD_ONLY_RECEIPT'])),
                'numerique_CC_INVIS_RECEIPT'        => sql::setBoolSql(Sql::boolSql($cc['CC_INVIS_RECEIPT'])),
                'numerique_CC_REQUIRED'             => sql::setBoolSql(Sql::boolSql($cc['CC_REQUIRED'])),
                'numerique_CC_PRINTED_OF'           => sql::setBoolSql(Sql::boolSql($cc['CC_PRINTED_OF'])),
                'numerique_CC_CALC'                 => sql::setBoolSql(Sql::boolSql($cc['CC_CALC'])),
                'CC_CALC_EXP'                       => $cc['CC_CALC_EXP'],
                'CC_VALUE'                          => $cc['VALEUR'],
                'DOC_AMD'                           => $amd
            ]);
        }
    }

    /**
     * updateCC
     *
     * @param  string $value
     * @param  string $docType
     * @param  int $docNum
     * @param  string $docLineNum
     * @param  string $idUserBasket
     * @param  string $ccId
     * @return void
     */
    public function updateCC(string $value, string $docType, int $docNum, string $docLineNum, string $idUserBasket, string $ccId): void
    {
        $ccLDoc = new CCLDoc($this->pdo);

        $ccLDoc->update([
            'CC_VALUE'          => $value,
        ], [
            'DOC_TYPE'          => $docType,
            'DOC_NUM'           => $docNum,
            'DOC_LINE_NUM'      => $docLineNum,
            'ID_USER_BASKET'    => $idUserBasket,
            'CC_ID'             => $ccId
        ]);
    }

    /**
     * majCcCalc
     *
     * @param  array<mixed> $tagsAChercher
     * @param  string $docType
     * @param  string $docNum
     * @param  string $idLigneDoc
     * @param  string $idPanier
     * @param  string $lineType
     * @param  bool $onlyCalc
     * @param  string $amd
     * @return void
     */
    public function updateCalculatedCc(array $tagsAChercher, string $docType, string $docNum, string $idLigneDoc, string $idPanier = '0', string $lineType = '', bool $onlyCalc = false, $amd = ''): void
    {
        // Récupération des CC calculés
        $ccACalculer = [];
        $listeCCLigne = $this->getCCDocc($docType, $docNum, $idLigneDoc, $idPanier, $onlyCalc, $amd);
        foreach ($listeCCLigne as $cc) {
            if (Sql::boolSql($cc['CC_CALC']) == 1) {
                $calculatedCc = preg_split('/(<[^>]*[^\/]>)/i', $cc['CC_CALC_EXP'], -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);

                if (is_array($calculatedCc)) {
                    foreach ($calculatedCc as &$tag) {
                        $tag = str_replace(['<', '>'], '', $tag);
                        if (in_array($tag, $tagsAChercher)) {
                            if (!isset($ccACalculer[$cc['CC_ID']])) {
                                $ccACalculer[$cc['CC_ID']] = ['CC_CALC_EXP' => $cc['CC_CALC_EXP'], 'decimal' => $cc['PD_LONG']];
                            }
                        }
                    }
                }
            }
        }

        $pu = 0;
        $qte = 0;
        $tva =0;
        $lignesBR = [];

        // MAJ DES CC calculés
        if (count($ccACalculer) > 0) {
            if ($docType == 'PAN') {
                $panierLigne = new PanierLigne($this->pdo);
                $lignePanier = $panierLigne->getAllLignePanier($_SESSION['id_panier'], $idLigneDoc);
                $pu = $lignePanier['PX_ART'];
                $qte = $lignePanier['qte'];
                $tva = $lignePanier['TAUX_TVA'];
            } elseif ($docType == 'BRT') {
                $enteteBr = $this->creationBr->getInfoBrEntete($_SESSION['Session_Login']);
                if ($lineType == 'BR') {
                    $lignesBR = $this->creationBr->getOxaBrLigne($_SESSION['Session_Login'], $idLigneDoc);
                }
                $pu = $lignesBR['PU_REMISE'];
                $qte = $lignesBR['qte_a'];
                $tva = $this->creationBr->getOrderLines($enteteBr['no_cde'], $idLigneDoc)[0]['TVA'];
            } elseif ($docType == 'AOT') {
                // TODO SPEC 4241 : recherche les éléments de la ligne d'AO
                // $pu = $lignePanier['PU_REMISE'];
                // $qte = $lignePanier['qte_a'];
                // $tva = $lignePanier['TAUX_TVA'];
            }

            foreach ($ccACalculer as $idCcToUpdate => $params) {
                $formule = preg_split('/(<[^>]*[^\/]>)/i', $params['CC_CALC_EXP'], -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);

                if (is_array($formule)) {
                    foreach ($formule as &$element) {
                        $element = str_replace(['<', '>'], '', $element);
                        if (strpos($element, 'CC_') !== false) {
                            $idCcForValue = substr($element, 3);
                            $listeCCLigne = $this->getCCDocc($docType, $docNum, $idLigneDoc, $idPanier, $onlyCalc, $amd);
                            $element = ($listeCCLigne[$idCcForValue]['VALEUR'] == '' || !is_numeric($listeCCLigne[$idCcForValue]['VALEUR'])) ?
                                0 : floatval($listeCCLigne[$idCcForValue]['VALEUR']);
                        } elseif ($element == 'PU') {
                            $element = floatval($pu);
                        } elseif ($element == 'MNT_LIG_HT') {
                            $element = '(' . floatval($pu) . '*' . floatval($qte) . ')';
                        } elseif ($element == 'MNT_LIG_TTC') {
                            $element = '(' . floatval($pu) . '*' . floatval($qte) . '*(1+' . $tva . '/100))';
                        } else {
                            //$element = $element;
                        }
                    }
                
                    $formule = implode($formule);
                }

                // Calcul de la formule
                try {
                    $formule = eval('$evalFormule = number_format((' . $formule . '), ' . $params['decimal'] . ', ".", "");');
                } catch (Throwable $t) {
                    $evalFormule = number_format(0, $params['decimal'], ".", "");
                }
                if (!is_finite((float)($evalFormule ?? 0))) {
                    $evalFormule = number_format(0, $params['decimal'], ".", "");
                }

                $this->updateCC($evalFormule ?? '', $docType, (int)$docNum, $idLigneDoc, $idPanier, (string)$idCcToUpdate);
            }
        }
    }
}
