<?php
//$Id$ 
//gen openMairie le 03/11/2021 12:39

require_once "../gen/obj/signataire_arrete.class.php";

class signataire_arrete extends signataire_arrete_gen {

    /**
     *
     * @return array
     */
    function get_var_sql_forminc__champs() {
        // TODO : voir si il y a moyen de modifier l'organisation des champs
        // en évitant d'avoir a chaque modif de la table a modifier cette liste
        // de champs
        return array(
            "signataire_arrete",
            "civilite",
            "nom",
            "prenom",
            "qualite",
            "signataire_habilitation",
            "agrement",
            "visa",
            "visa_2",
            "visa_3",
            "visa_4",
            "signature",
            "defaut",
            "om_validite_debut",
            "om_validite_fin",
            "om_collectivite",
            "email",
            "description",
            "habilitation_parapheur",
            "parametre_parapheur",
        );
    }
    
    function setType(&$form, $maj) {
        parent::setType($form, $maj);

        // Récupération du mode de l'action
        $crud = $this->get_action_crud($maj);

        // MODE CREER
        if ($maj == 0 || $crud == 'create') {
            $form->setType("parametre_parapheur", "textarea");
            $form->setType('habilitation_parapheur', 'select');
        }
        // MDOE MODIFIER
        if ($maj == 1 || $crud == 'update') {
            $form->setType("parametre_parapheur", "textarea");
            $form->setType('habilitation_parapheur', 'select');
        }
        // MODE CONSULTER
        if ($maj == 3 || $crud == 'read') {
            $form->setType('parametre_parapheur', 'textareastatic');
            $form->setType('habilitation_parapheur', 'selectstatic');
        }
    }

    function setvalF($val = array()) {
        // Valeur par défaut des habilitations dans le parapheur pour un signataire
        if (empty($val['habilitation_parapheur'])) {
            $val['habilitation_parapheur'] = "signer";
        }
        parent::setValF($val);
        if (array_key_exists('parametre_parapheur', $val) === true) {
            $this->valF['parametre_parapheur'] = str_replace("'", '"', $val['parametre_parapheur']);
        }
    }

    function setLib(&$form, $maj) {
        parent::setLib($form, $maj);
        $form->setLib('habilitation_parapheur', __('habilitations dans le parapheur'));
    }

    /**
     * Surcharge pour ajouter l'action de renvoi en signature en lot lors d'un changement
     * du mode de signature.
     */
    public function init_class_actions() {
        parent::init_class_actions();

        // on affiche toujours ce bouton d'action (cf specs)
        $this->class_actions[14] = array(
            "identifier" => "renvoyer_en_signature",
            "portlet" => array(
                "type" => "action-direct-with-confirmation",
                "libelle" => __("Relancer les demandes de signature"),
                "class" => "envoyer_a_signature-16"),
            "view" => "formulaire",
            "method" => "view_renvoyer_en_signature",
            "crud" => "read",
            "permission_suffix" => "renvoyer_en_signature",
        );
    }

    public function view_renvoyer_en_signature() {
        $this->begin_treatment(__METHOD__);
        $this->correct = true;
        $err_base = __("Échec du renvoie en signature.");

        // récupère les infos sur les documents à renvoyer en signature
        $res = $this->get_all_documents_sent_to_be_signed();
        if ($res === false) {
            $this->correct = false;
            $this->addToMessage(__("Erreur de base de donnée. Contactez votre administrateur."));
            return $this->end_treatment(__METHOD__, false);
        }
        list($docs_data, $item_count) = $res;
        $this->f->log(__METHOD__, "documents_sent_to_be_signed: ".var_export($docs_data, true));
        $this->f->log(__METHOD__, "item count: ".var_export($item_count, true));

        // aucun document à renvoyer en signature
        if ($item_count == 0) {
            $this->addToMessage(__("Aucun document à renvoyer en signature."));
            return $this->end_treatment(__METHOD__, true);
        }

        // il y a des documents à renvoyer en signature
        if ($item_count > 0) {

            // stocke le résultat des opérations
            $failed_cancel = array();
            $failed_sent = array();

            // pour chacun des documents
            foreach($docs_data as $doc_data) {

                // vérification que les données requises sont bien présentes
                $paraph_id = $doc_data['id_parapheur_signature'] ?? null;
                $dossier_lib = $doc_data['dossier_libelle'] ?? null;
                $instr_id = $doc_data['instruction'] ?? null;
                $evt_lib = $doc_data['libelle'] ?? null;
                if (empty($dossier_lib)) {
                    $this->correct = false;
                    $err_msg = __("Erreur: dossier sans libellé.").' '.
                        __("Détail:").' '.var_export($doc_data, true);
                    $failed_cancel[] = $err_msg;
                    continue;
                }
                if (empty($instr_id)) {
                    $this->correct = false;
                    $err_msg = __("Erreur: instruction sans identifiant.").' '.
                        __("Détail:").' '.var_export($doc_data, true);
                    $failed_cancel[] = $err_msg;
                    continue;
                }
                if (empty($paraph_id)) {
                    $this->correct = false;
                    $err_msg = sprintf(
                        __("L'instruction <em>%s</em> (%s #%s) n'a pas d'identifiant parapheur"),
                        $evt_lib, $dossier_lib, $instr_id);
                    $failed_cancel[] = $err_msg;
                    continue;
                }

                // récupère l'instruction concernée
                $instr_inst = $this->f->findObjectById('instruction', $instr_id);
                if (empty($instr_inst)) {
                    $this->correct = false;
                    $err_msg = sprintf(
                        __("Échec de récupération des données de l'instruction <em>%s</em> (%s #%s)"),
                        $evt_lib, $dossier_lib, $instr_id);
                    $failed_cancel[] = $err_msg;
                    continue;
                }

                // s'il n'y a plus de connecteur parapheur configuré
                if (! $instr_inst->has_connector_electronicsignature()) {
                    $this->correct = false;
                    $collectivite = $instr_inst->get_dossier_instruction_om_collectivite();
                    $err_msg = sprintf(
                        __("Il n'y a plus de connecteur parapheur configuré pour la collectivité <em>%s</em> (instruction <em>%s</em> (%s #%s))"),
                        $collectivite, $evt_lib, $dossier_lib, $instr_id);
                    $failed_cancel[] = $err_msg;
                    continue;
                }

                // récupère une instance du connecteur parapheur
                $es_inst = $instr_inst->get_electronicsignature_instance();
                if (empty($es_inst)) {
                    $this->correct = false;
                    $err_msg = sprintf(
                        __("Échec lors de l'initialisation du connecteur parapheur (instruction <em>%s</em> (%s #%s))"),
                        $evt_lib, $dossier_lib, $instr_id
                    );
                    $failed_cancel[] = $err_msg;
                    continue;
                }

                // Vérifie que le connecteur permet l'annulation d'un parapheur
                if (empty($es_inst->get_conf('cancel_send')) === true
                    || $es_inst->get_conf('cancel_send') === false) {
                    //
                    $this->correct = false;
                    $err_msg = sprintf(
                        __("Le connecteur parapheur configuré ne permet pas l'annulation d'un parapheur (instruction <em>%s</em> (%s #%s))"),
                        $evt_lib, $dossier_lib, $instr_id
                    );
                    $failed_cancel[] = $err_msg;
                    continue;
                }

                // forçage de la relecture
                $review_before_signing = (
                    $es_inst->get_conf('is_forced_view_files') === 'true'
                    || $es_inst->get_conf('is_forced_view_files'));

                // annule l'envoie en signature
                if (($cancellation = $instr_inst->annuler_envoi_en_signature()) === false) {
                    $this->correct = false;
                    $err_msg = sprintf(
                        __("Échec de l'annulation du parapheur <em>%s</em> (instruction: <em>%s</em> (%s #%s))."),
                        $paraph_id, $evt_lib, $dossier_lib, $instr_id
                    );
                    if (! empty($instr_inst->msg)) {
                        $err_msg .= ' '.__("Détail:").' '.$instr_inst->msg;
                    }
                    $failed_cancel[] = $err_msg;
                    continue;
                }

                // renvoi en signature
                if (($resending = $instr_inst->envoyer_a_signature($review_before_signing)) === false) {
                    $this->correct = false;
                    $err_msg = sprintf(
                        __("Échec de l'envoi en signature de l'instruction: <em>%s</em> (%s #%s))."),
                        $evt_lib, $dossier_lib, $instr_id
                    );
                    if (! empty($instr_inst->msg)) {
                        $err_msg .= ' '.__("Détail:").' '.$instr_inst->msg;
                    }
                    $failed_sent[] = $err_msg;
                    continue;
                }
            }

            // calcul combien de parapheur ont été renvoyé avec succès
            // en le déduisant du nombre d'échecs par rapport au nombre initial.
            $failed_cancel_count = count($failed_cancel);
            $failed_sent_count = count($failed_sent);
            $ok_count = $item_count - $failed_cancel_count - $failed_sent_count;

            // construit le message pour l'utilisateur
            $msg_lines = array();
            if ($ok_count > 0) {
                $msg_lines[] = sprintf(__("<strong>%d parapheur(s) renvoyé(s) en signature avec succès.</strong>"), $ok_count);
                $msg_lines[] = '';
            }
            if ($failed_cancel_count > 0) {
                $msg_lines[] = sprintf(__("<strong>%d parapheur(s) dont l'annulation a échoué :</strong>"), $failed_cancel_count);
                $msg_lines[] = '';
                $msg_lines = array_merge($msg_lines, array_map(
                    function ($i) { return ' - '.$i; },
                    $failed_cancel));
            }
            if ($failed_sent_count > 0) {
                $msg_lines[] = sprintf(__("<strong>%d parapheur(s) dont l'envoi a échoué :</strong>"), $failed_sent_count);
                $msg_lines[] = '';
                $msg_lines = array_merge($msg_lines, array_map(
                    function ($i) { return ' - '.$i; },
                    $failed_sent));
            }

            // affiche le message
            $msg = implode("\n", $msg_lines);
            $this->f->log(__METHOD__, $msg, 'INFO');
            $msg = implode('</br>', $msg_lines);
            $this->addToMessage($msg);
        }

        return $this->end_treatment(__METHOD__, true);
    }

    /**
     * Surcharge pour permettre la détection des parapheurs déjà envoyé en signature par le
     * signataire lorsque ses nouveaux paramètres indique un changement de mode de signature.
     * Initialement le besoin est de pouvoir signer sans carte agent.
     */
    public function triggermodifierapres($id, &$dnu1 = null, $val = array(), $dnu2 = null) {

        $res = parent::triggermodifierapres($id, $dnu1, $val, $dnu2);

        $params_str = $this->valF['parametre_parapheur'] ?? null;
        $params = array();
        if (! empty($params_str) && $params_str != '{}') {
            $params = json_decode($params_str, true);
            $json_err = $this->check_json_error();
            if ($json_err !== false) {
                $err_msg = "Erreur pendant le décodage en JSON: $json_err";
                $this->f->log(__METHOD__, $err_msg, 'ERROR');
                $this->correct = false;
                $this->addToMessage(__("Échec de l'enregistrement des données").' '.__("Détail:").' '.$err_msg);
                return false;
            }
        }
        $this->f->log(__METHOD__, "params: ".var_export($params, true));

        $old_params_str = $this->getVal('parametre_parapheur');
        $old_params = array();
        if (! empty($old_params_str) && $old_params_str != '{}') {
            $old_params = json_decode($old_params_str, true);
            $json_err = $this->check_json_error();
            $this->f->log(__METHOD__, "parametre_parapheur (json): ".var_export($old_params, true));
            if ($json_err !== false) {
                $err_msg = "Erreur pendant le décodage en JSON: $json_err";
                $this->f->log(__METHOD__, $err_msg, 'ERROR');
                $this->correct = false;
                $this->addToMessage(__("Échec de l'enregistrement des données").' '.__("Détail:").' '.$err_msg);
                return false;
            }
        }
        $this->f->log(__METHOD__, "old params: ".var_export($old_params, true));

        // si les nouveaux paramètres sont différents, changent le mode de signature, et spécifient
        // désormais une signature sans carte agent
        if ($this->params_have_changed($params, $old_params)
                && $this->params_changes_signing_mode($params, $old_params)
                && $this->params_signing_mode_is_fallback($params)) {

            $res = $this->get_all_documents_sent_to_be_signed();
            if ($res === false) {
                $this->correct = false;
                $this->addToMessage(__("Erreur de base de donnée. Contactez votre administrateur."));
                return false;
            }
            list($docs_data, $item_count) = $res;
            $this->f->log(__METHOD__, "documents_sent_to_be_signed: ".var_export($docs_data, true));
            $this->f->log(__METHOD__, "item count: ".var_export($item_count, true));

            // il y a des documents à renvoyer en signature
            if ($item_count > 0) {

                // initialise un message qui sera affiché à l'utilisateur
                $msg = __("Changement de mode de signature détecté.").' '.
                       __("Des demandes de signature sont en cours pour ce signataire.").' '.
                       sprintf(
                           __("Vous pouvez relancer les '%d' demandes de signature en cours pour prendre en compte la nouvelle modalité de signature"),
                           $item_count);
                $this->addToMessage($msg);
            }
        }
    }

    protected function params_have_changed(array $params, array $old_params) {
        $this->f->log(__METHOD__, "params changed ? ".var_export($params != $old_params, true));
        return $params != $old_params;
    }

    protected function params_changes_signing_mode(array $params, array $old_params) {
        $params_new_signing_mode_is_fallback = $this->params_signing_mode_is_fallback($params);
        $params_old_signing_mode_is_fallback = $this->params_signing_mode_is_fallback($old_params);
        $this->f->log(__METHOD__, "params signature mode changed ? ".var_export($params_new_signing_mode_is_fallback != $params_old_signing_mode_is_fallback, true));
        return $params_new_signing_mode_is_fallback != $params_old_signing_mode_is_fallback;
    }

    protected function params_signing_mode_is_fallback(array $params) {
        return isset($params['modalite_signature'])
             && $params['modalite_signature'] == 'sans_carte_agent';
    }

    protected function get_all_documents_sent_to_be_signed() {

        // prépare une requête SQL pour rechercher tous les documents déjà envoyé en signature
        // par ce signataire (qui ne sont pas encore signés)
        $signataire_id = $this->getVal($this->clePrimaire);
        $sql = sprintf("
            SELECT
                d.dossier_libelle AS dossier_libelle,
                i.instruction AS instruction,
                e.libelle AS libelle,
                i.id_parapheur_signature AS id_parapheur_signature
            FROM
                %1\$sinstruction AS i
                LEFT JOIN %1\$sdossier AS d ON d.dossier = i.dossier
                LEFT JOIN %1\$sevenement AS e ON e.evenement = i.evenement
            WHERE
                i.signataire_arrete = %2\$s
                AND i.statut_signature NOT IN ('finished', 'canceled', 'expired')
                AND i.statut_signature IS NOT NULL
                AND i.statut_signature != ''
            ORDER BY
                i.instruction
        ", DB_PREFIXE, $this->f->db->escapeSimple($signataire_id));
        $qres = $this->f->get_all_results_from_db_query($sql, array('origin' => __METHOD__));
        if ($qres['code'] !== 'OK') {
            $err_msg = $qres['message'] ?? 'Erreur de base de donnée';
            $this->f->log(__METHOD__, $err_msg, 'ERROR');
            return false;
        }
        return array($qres['result'], $qres['row_count']);
    }

    /**
     * Vérifie si une erreur JSON a eut lieu, et dans ce cas renvoi un message d'erreur,
     * 'false' (bool) sinon.
     *
     * @return bool|string
     */
    protected function check_json_error() {
        if (json_last_error() !== JSON_ERROR_NONE) {
            return json_last_error_msg();
        }
        return false;
    }
    
    /**
     * SETTER_FORM - setSelect.
     *
     * @return void
     */
    function setSelect(&$form, $maj, &$dnu1 = null, $dnu2 = null) {
        parent::setSelect($form, $maj);
        // habilitation en lien avec le parapheur
        $crud = $this->get_action_crud($maj);
        $nature = array(
            array("", "valider", "signer", "valider_signer"),
            array($crud === 'read' ? "" : __("Choisir habilitation dans le parapheur"), __("Peut valider"), __("Peut signer"), __("Peut valider et signer")),
        );
        $form->setSelect("habilitation_parapheur", $nature);
    }
    
    /**
     * SETTER_FORM - setLayout.
     *
     * @return void
     */
    function setLayout(&$form, $maj) {
        $data = array('form' => &$form, 'maj' => &$maj);
        $this->f->module_manager->run_hooks('setLayout_pre', $this, $data);

        // Récupère le CRUD
        $crud = $this->get_action_crud($this->getParameter("maj"));
            $form->setBloc('signataire_arrete', 'D');
            $form->setBloc('email', 'F');
            $form->setBloc('lib-description', 'DF');
            // Parapheur électronique
            $form->setBloc('habilitation_parapheur', 'D', __("Parapheur électronique"), 'parapheur');
                $form->setBloc('habilitation_parapheur', 'D', '', 'habilitation_parapheur');
                $form->setBloc('parametre_parapheur', 'F', '', 'parametre_parapheur');
            $form->setBloc('parametre_parapheur', 'F','', '');
    }
}
