<?php
namespace Modules\Commande\Controller;

use Entity\Model\CaEntet;
use Entity\Model\CaEvent;
use Entity\Model\CaLigne;
use Entity\Model\Codecomp;
use Entity\Model\Codgest;
use Entity\Model\Fourniss;
use Entity\Model\MCa;
use Entity\Model\Parametr;
use Entity\Model\Paravrac;
use Entity\Model\Sectimpu;
use Entity\Model\Sscptana;
use GuzzleHttp\Psr7\Response;
use Helpers\Ariane\Ariane;
use Helpers\Log\LogSysteme;
use Helpers\Outil\Roles;
use Helpers\Outil\Sql;
use Helpers\Trad\Trad;
use Helpers\Validator\Validator;
use Modules\Commande\Repository\Commande;
use Modules\Commande\Repository\ModificationDetailCommande;
use Skeleton\Controller\Controller;
use Traits\Validator\Zoom;

/**
 * @see \Modules\Commande\Rules\Imputation
 */
class ImputationController extends Controller
{

    /**
     * @return Response|string
     */
    public function formUpdateAction()
    {
        ///oxaproc2/V2/Commande/modification/massImputation/773
        $commandeId = $this->request->getAttribute('commandeId');
        $trad = $this->getTranslationService();

        $commande = (new CaEntet($this->pdo))->find($commandeId);
        if (empty($commande)) {
            return $this->create404Response();
        }

        $commande['Fourniss'] = (new Fourniss($this->pdo))
            ->find($commande['CODE_FOU']);
        if (!empty($commande['ID_CG'])) {
            $commande['CodeGestion'] = (new Codgest($this->pdo))->find([
                'CODE_STE' => $commande['CODE_STE'],
                'CODE_SI' => $commande['SECTIMP'],
                'CODE_GES' => $commande['ID_CG'],
            ]);
        }

        if (!empty($commande['SECTIMP'])) {
            $commande['SectImput'] = (new Sectimpu($this->pdo))->find([
                'CODE_STE' => $commande['CODE_STE'],
                'CODE_SI' => $commande['SECTIMP'],
            ]);
        }

        $modelCa = (new MCa($this->pdo))->find($commande['MODELE']);
        
        Ariane::generate(1, $trad->t('Modification des imputations'));

        return $this->renderer->render('@Commande/Imputation/imputationForm', [
            'caEntet' => $commande,
            'config' => (new Paravrac($this->pdo))->getCommandeAxeImpConfig(),
            'canUpdateSectImp' =>
                $this->hasRight('HA_CA_MSI')
                && !(sql::toBool($modelCa['SIVER'] /*Model lock SI*/ ?? 'F')),
            'canUpdateCodeComp' =>
                $this->hasRight('HA_CA_MCD')
                && !(sql::toBool($modelCa['CDCVER'] /*Model lock CDC*/ ?? 'F')),
            'canUpdateSousComptAna' =>
                $this->hasRight('HA_CA_MSC')
                && !(sql::toBool($modelCa['SCAVER'] /*Model lock SCA*/ ?? 'F')),
            'listCaLigne' => (new Commande($this->pdo))
                ->listeLigneCommande($commande['NO_CDE'], true)
        ]);
    }

    public function updateAction() :Response
    {
        $commandeId = $this->request->getAttribute('commandeId');
        $commande = (new CaEntet($this->pdo))->find($commandeId);
        if (empty($commande)) {
            return $this->create404Response();
        }
        $post = $this->request->getParsedBody();

        $trad = $this->getTranslationService();

        $listeCaLigneId = [];
        foreach ($post as $key => $val) {
            if (strpos($key, 'ligne_') !== 0) {
                continue;
            }
            $listeCaLigneId[] = substr($key, 6);
        }

        $droit = Roles::droitsUtilMulti($this->pdo, [
            'HA_CA_MSI', // Modif SI sur Cde validée
            'HA_CA_MCG', // Modif CG sur Cde validée
            'HA_CA_MSC', // Modif SCA sur Cde validée
            'HA_CA_MCD', // Modif CDC sur Cde validée
        ]);

        $hasSectImpModif = $post['MODIF_SECTIMP'] ?? false;
        $hasCodeCompModif = $post['MODIF_CODE_COMP'] ?? false;
        $hasSousComptAnaModif = $post['MODIF_SSCPTEANA'] ?? false;

        //_____________________________
        // Validation

        $validator = $this->createValidator();

        if (empty($listeCaLigneId)) {
            $validator->setError(
                $trad->t('Vous devez sélectionner au moins une ligne.')
            );
        }

        // Config
        $paramtr = (new Parametr($this->pdo));
        $qbParam = (new Paravrac($this->pdo));
        $configAxeImp = $qbParam->getCommandeAxeImpConfig($commande['CODE_STE']);

        // Failsafe
        $modelCa = (new MCa($this->pdo))->find($commande['MODELE']);
        if ($modelCa != null) {
            // Case : model lock SectImput edition
            if (sql::toBool($modelCa['SIVER']) && $hasSectImpModif) {
                throw new \Exception('Trying to edit SI with a lock on its Model');
            }
            // Case : model lock CodeComp edition
            if (sql::toBool($modelCa['CDCVER']) && $hasCodeCompModif) {
                throw new \Exception('Trying to edit CDC with a lock on its Model');
            }
            // Case : model lock SousComptAna edition
            if (sql::toBool($modelCa['SCAVER']) && $hasSousComptAnaModif) {
                throw new \Exception('Trying to edit SCA with a lock on its Model');
            }
        }
        if (!$configAxeImp->SectImp->isActive && $hasSectImpModif) {
            throw new \Exception('Trying to edit SI which is disabled');
        }
        if (!$configAxeImp->CodeCompl->isActive && $hasCodeCompModif) {
            throw new \Exception('Trying to edit CDC which is disabled');
        }
        if (!$configAxeImp->SousCompteAna->isActive && $hasSousComptAnaModif) {
            throw new \Exception('Trying to edit SCA which is disabled');
        }

        $arrayEdit = [];
        // TODO : simplify
        if ($hasSectImpModif && $droit['HA_CA_MSI']) {
            $validator->zoom(
                'Sectimpu',
                $post['SECTIMP_ENTETE'],
                $trad->t('Section d’imputation'),
                $this->pdo,
                $configAxeImp->SectImp->isBlankAllowed,
                ($configAxeImp->SectImp->isSecto ?
                    ['CODE_STE' => $commande['CODE_STE']] : [])
            );

            $sectionImp = (new Sectimpu($this->pdo))->find(
                ['CODE_SI' => $post['SECTIMP_ENTETE']]+($configAxeImp->SectImp->isSecto ?['CODE_STE' => $commande['CODE_STE']] : [])
            );

            // Denormalize
            if ($sectionImp != null) {
                $sectionImp['SICODG'] = Sql::toBool($sectionImp['SICODG']);
                $sectionImp['IMPCODG'] = Sql::toBool($sectionImp['IMPCODG']);
            }

            // Validate SectImpu date
            if ($sectionImp != null) {
                $validator->entityDateValidation(
                    CaLigne::class,
                    Sectimpu::class,
                    $trad,
                    $sectionImp['DATE_DEB'],
                    $sectionImp['DATE_FIN'],
                    $commande['DTE_PERIO']
                );
            }

            // Case : SectImp can have a CodeGest
            if (isset($sectionImp) && $sectionImp['SICODG']) {
                $codeGest = (new Codgest($this->pdo))
                    ->findEntity(
                        $sectionImp['CODE_STE'],
                        $sectionImp['CODE_SI'],
                        $post['CODGES_ENTETE']
                    );

                // Case : Given CodeGest invalid
                if (!empty(trim($post['CODGES_ENTETE']??''))
                    && $codeGest == null
                ) {
                    $validator->setError(
                        Trad::strBuild(
                            $trad->t('Le code saisi @1@ n\'est pas présent dans la liste des @2@.'),
                            $post['CODGES_ENTETE'],
                            $trad->t('Code gestion')
                        )
                    );
                }

                // Case : SectImp require a CodeGest
                if ($sectionImp['IMPCODG'] && $codeGest == null) {
                    $validator->setError(
                        Trad::strBuild(
                            $trad->t('Le champ @1@ ne peut pas être vide.'),
                            $trad->t('Code gestion')
                        )
                    );
                }

                if ($codeGest != null) {
                    // Validate CodeGest
                    $validator->entityDateValidation(
                        CaLigne::class,
                        Codgest::class,
                        $trad,
                        $codeGest['DTEVALMIN'],
                        $codeGest['DTEVALMAX'],
                        $commande['DTE_PERIO']
                    );
                }
            }

            $arrayEdit['SECTIMP'] = trim($post['SECTIMP_ENTETE']);

            $arrayEdit['ID_CG'] = $codeGest['CODE_GES']??'';
            $arrayEdit['CODGES'] = $codeGest['CODE_AFF']??'';
            $arrayEdit['LIBGES'] = $codeGest['LIB_GES']??'';
        }

        if ($droit['HA_CA_MCD'] && $hasCodeCompModif) {
            $validator->zoom(
                'Codecomp',
                $post['CODE_COMP_ENTETE'],
                $trad->t('Code complémentaire'),
                $this->pdo,
                $configAxeImp->CodeCompl->isBlankAllowed,
                ($configAxeImp->CodeCompl->isSecto ?
                    ['CODE_STE' => $commande['CODE_STE']] : [])
            );

            $codeComp = (new Codecomp($this->pdo))->find(
                ['CODE_CDC' => $post['CODE_COMP_ENTETE']]+($configAxeImp->CodeCompl->isSecto ?['CODE_STE' => $commande['CODE_STE']] : [])
            );

            if ($codeComp != null) {
                $validator->entityDateValidation(
                    CaLigne::class,
                    Codecomp::class,
                    $trad,
                    $codeComp['DATE_DEB'],
                    $codeComp['DATE_FIN'],
                    $commande['DTE_PERIO']
                );
            }

            $arrayEdit['CODE_COMP'] = trim($post['CODE_COMP_ENTETE']);
        }

        if ($droit['HA_CA_MSC'] && $hasSousComptAnaModif) {
            $validator->zoom(
                'Sscptana',
                $post['SSCPTEANA_ENTETE'],
                $trad->t('Sous compte d\'analyse'),
                $this->pdo,
                $configAxeImp->SousCompteAna->isBlankAllowed,
                ($configAxeImp->SousCompteAna->isSecto ?
                    ['CODE_STE' => $commande['CODE_STE']] : [])
            );

            $sousComptAna = (new Sscptana($this->pdo))->find(
                ['CODE_SCA' => $post['SSCPTEANA_ENTETE']]+($configAxeImp->SousCompteAna->isSecto ?['CODE_STE' => $commande['CODE_STE']] : [])
            );

            if ($sousComptAna != null) {
                $validator->entityDateValidation(
                    CaLigne::class,
                    Sscptana::class,
                    $trad,
                    $sousComptAna['DATE_MINI'],
                    $sousComptAna['DATE_MAXI'],
                    $commande['DTE_PERIO']
                );
            }

            $arrayEdit['SSCPTEANA'] = trim($post['SSCPTEANA_ENTETE']);
        }

        if (empty($arrayEdit)) {
            $validator->setError(
                $trad->t('Vous devez sélectionner au moins une action.')
            );
        }

        if (!$validator->isValid()) {
            return new Response(
                400,
                [],
                $this->renderer->render('@popup-erreur/ErreurCreation', [
                    'ErreurValid' => $validator->getError(),
                ])
            );
        }

        if ($validator->isNonBloquant() && !isset($post['confirm'])) {
            return $this->createJsonResponse(
                [
                    'type' => 'confirm',
                    'popupHtml' => $this->renderer->render('@popup-erreur/simpleWarning', [
                        'content' => $validator->getErrorNonBloquant(),
                    ]),
                ],
                400
            );
        }

        //_____________________________
        // Update DB

        $listCaLigne = (new CaLigne($this->pdo))->findAll([
            'NO_CDE' => $commandeId,
            'NO_PO' => $listeCaLigneId,
        ]);

        // Filter those with no modification
        $listCaLigne = array_filter(
            $listCaLigne,
            function (array $caLigne) use ($arrayEdit) {
                foreach ($arrayEdit as $key => $value) {
                    if ($caLigne[ $key ] != $value) {
                        return true;
                    }
                }
                return false;
            }
        );

        $repo = new ModificationDetailCommande($this->pdo);

        try {
            $repo->procDecumul($commandeId);
        } catch (\Exception $e) {
            LogSysteme::prefixErrorLog((string)$e);
            return new Response(
                400,
                [],
                $this->renderer->render('@popup-erreur/ErreurCreation', [
                    'ErreurValid' => $e->getMessage(),
                ])
            );
        }

        // TODO : use caLigne entity update instead
        $repo->updateMasse($commandeId, $arrayEdit, $listeCaLigneId);
        
        (new CaEvent($this->pdo))->insertEntity(
            $commande['NO_CDE'],
            $commande['AVENANT'],
            Sql::toBool($commande['SIGNEE']) ? 'MIS' : 'MIV',
            $_SESSION['Session_Login'],
            Sql::updateMemo(
                $this->pdo,
                '',
                $this->renderer->render(
                    '@Commande/Imputation/imputationUpdateEvent',
                    [
                        'listCaLigne' => $listCaLigne,
                        'listUpdate' => array_filter(array_map(function ($key, $value) {
                            if (!in_array($key, ['SECTIMP', 'SSCPTEANA', 'CODE_COMP','ID_CG'])) {
                                return null;
                            }
                
                            return [
                                'label' => $this->getImpLabel($key),
                                'key' => $key,
                                'value' => $value,
                            ];
                        }, array_keys($arrayEdit), $arrayEdit)),
                    ]
                )
            )
        );

        try {
            $repo->procCumul($commandeId);
        } catch (\Exception $e) {
            LogSysteme::prefixErrorLog((string)$e);
        }
        

        //_____________________________
        // render update

        return new Response(
            200,
            [],
            $this->renderer->render('@Commande/Imputation/imputationFormTable', [
                'config' => (new Paravrac($this->pdo))->getCommandeAxeImpConfig(),
                'listCaLigne' => (new Commande($this->pdo))
                    ->listeLigneCommande($commandeId)
            ])
        );
    }

//_____________________________________________________________________________
// Sub-routine

    private function getImpLabel(string $key) :string
    {
        // trad export : $trad->t('SCA'); $trad->t('CDC'); $trad->t('SI'); $trad->t('CG');
        switch ($key) {
            case 'SSCPTEANA':
                return 'SCA';
            case 'CODE_COMP':
                return 'CDC';
            case 'SECTIMP':
                return 'SI';
            case 'ID_CG':
                return 'CG';
            default:
        }
        LogSysteme::prefixErrorLog((new \Exception('Invalid imp col "'.$key.'"'))->__toString());
        return 'unknown';
    }

    /**
     * hasRight
     *
     * Shorthand for Roles::droitsUtilisateur
     * @see Roles::droitsUtilisateur()
     *
     * @param  string $right
     * @return bool
     */
    private function hasRight(string $right) :bool
    {
        return Roles::droitsUtilisateur($this->pdo, $right) == 1;
    }
}
