<?php
/**
 * Ce script contient la définition de la classe *om_widget*.
 *
 * @package openresultat
 * @version SVN : $Id$
 */

require_once PATH_OPENMAIRIE."obj/om_widget.class.php";

/**
 * Définition de la classe *om_widget*.
 */
class om_widget extends om_widget_core {
    public function view_widget__election($content = null) {
        $elections = array();
        $query = sprintf(
            'SELECT to_char(election.date ,\'DD/MM/YYYY\') as election_date, * FROM %1$selection WHERE is_centaine IS NOT TRUE AND workflow NOT LIKE \'Archivage\'',
            DB_PREFIXE
        );
        $res = $this->f->db->query($query);
        $this->f->isDatabaseError($res);
        while ($row = $res->fetchrow(DB_FETCHMODE_ASSOC)) {
            $elections[] = $row;
        }
        if (count($elections) == 0) {
            echo __("Aucune élection en cours.");
            return false;
        }
        $content = "";
        $workflow_icon = array(
            'Paramétrage'=> 'settings-3-line',  
            'Simulation'=> 'compasses-2-line',  
            'Saisie'=> 'edit-line',  
            'Finalisation'=> 'file-check-line',
        );
        $line_template = '
        <a href="%1$s" class="list-group-item list-group-item-action">
            <h4>%2$s
                <small style="display:block">%3$s</small>
            </h4>
            <span class="badge election-step %4$s"><i class="ri-%5$s"></i>%4$s</span>
        </a>
        ';
        $out = '<div class="list-group">';
        foreach ($elections as $key => $value) {
            $out .= sprintf(
                $line_template,
                OM_ROUTE_FORM."&obj=election&action=3&idx=".$value["election"],
                $value["libelle"],
                $value["election_date"],
                $value["workflow"],
                $workflow_icon[$value["workflow"]],

            );
        }
        $out .= '</div>';
        echo $out;
        return false;

    }

    public function view_widget__web($content = null) {
        $contenuWidget = 'Le portail web n\'est pas actif.';
        if ($this->portail_actif()) {
            $contenuWidget = sprintf(
                '<div class="link-web"><a href="%1$s" target="blank" title="Lien externe"><i class="ri-door-open-line"></i><h4>%2$s <i class="ri-external-link-line"></i></h4></a></div>',
                "../web/",
                __("Accéder au portail web de publication des résultats électoraux")
            );
        }
        echo $contenuWidget;
        return false;
    }

    /**
     * WIDGET_DASHBOARD - view_widget__animation.
     *
     * @return boolean
     */
    public function view_widget__animation($content = null) {
        $animations = array();
        $query = sprintf(
            'SELECT e1.election as election_id, e1.libelle as election_libelle, animation.libelle as animation_libelle, * FROM %1$sanimation INNER JOIN %1$selection as e1 on animation.election=e1.election LEFT JOIN %1$selection as e2 on e1.election_reference=e2.election WHERE ((e2.workflow IS NULL OR e2.workflow NOT LIKE \'Archivage\') AND e1.workflow NOT LIKE \'Archivage\') ORDER BY e1.date DESC, e1.election, animation.libelle',
            DB_PREFIXE
        );
        $res = $this->f->db->query($query);
        $this->f->isDatabaseError($res);
        while ($row = $res->fetchrow(DB_FETCHMODE_ASSOC)) {
            $animations[] = $row;
        }
        if (count($animations) == 0) {
            echo __("Aucune animation en cours.");
            return false;
        }
        $out = "<ul>";
        foreach ($animations as $key => $value) {
            $out .= sprintf(
                '<li><a target="blank" href="%1$s">%2$s</a></li>',
                '../aff/animation.php?idx='.$value["election_id"].'&identifier='.$value["animation"],
                $value["election_libelle"]." - ".$value["animation_libelle"]
            );
        }
        $out .= "</ul>";
        print($out);
        return false;
    }


    /**
     * Widget de saisie de participation électorale.
     *
     * Affiche un sélecteur des élections en cours et de leurs tranches de participations,
     * pour ouvrir une modale avec le formulaire de saisie
     *
     * @param string|null $content Contenu optionnel, non utilisé ici.
     * @return bool Toujours false pour indiquer la fin du traitement sans renvoyer de contenu.
     */
    public function view_widget__saisie_participation($content = null, int $widget_id = 0)
    {
        $arguments = $this->get_arguments($content, ['identifiant_election']);

        $elections_en_cours = $this->get_elections_en_cours($arguments['identifiant_election'] ?? null);
        if (!is_array($elections_en_cours)) {
            echo '<div class="message ui-state-info">', $elections_en_cours, '</div>';
            return false;
        }

        // Pour chaque élection en cours, on va récupérer les tranches horaires (pour le cas où il n'y a pas de délégation)
        $tranches_horaires = [];
        foreach ($elections_en_cours as $id => $election) {
            if ($this->f->boolean_string_to_boolean($election->getVal('delegation_participation'))) {
                continue;
            }

            $tranches_horaires[$id] = [];

            $sql = sprintf(<<<'SQL'
                SELECT participation_election, libelle
                FROM
                    %1$sparticipation_election
                    INNER JOIN %1$stranche USING (tranche)
                WHERE election = %2$d
                ORDER BY ordre
                SQL,
                DB_PREFIXE,
                $id,
            );
            $res = $this->f->db->query($sql);
            $this->f->isDatabaseError($res);

            // Oui, on fait des requêtes SQL dans des boucles par la suite, 
            // mais on ne devrait pas avoir trop d'élections en cours en même temps
            while ($r = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
                array_push($tranches_horaires[$id], [
                    'value' => intval($r['participation_election']),
                    'text' => $r['libelle'],
                ]);
            }
        }

        // Pour chaque élection en cours, on va lister les unités avec délégation (pour les cas où il y a délégation)
        $unites_par_election = [];
        foreach ($elections_en_cours as $id => $election) {
            if (!$this->f->boolean_string_to_boolean($election->getVal('delegation_participation'))) {
                continue;
            }

            $unites_par_election[$id] = [];
            foreach ($election->get_unites_autorisees_for_user($_SESSION['login'], 'participation') as $u) {
                array_push($unites_par_election[$id], [
                    'value' => intval($u['election_unite']), // On prends l'ID de l'élection_unite et pas de l'unite elle-même, car c'est l'election_unite qui porte les résultatss
                    'text' => "{$u['code_unite']} - {$u['libelle']}",
                ]);
            }
        }

        // On construit la liste des options
        $options_elections = array_map(
            fn (int $id) => sprintf(<<<'HTML'
                <option value="%1$d" data-tranches='%3$s' data-unites='%4$s'>%1$d - %2$s</option>
                HTML,
                $id,
                $elections_en_cours[$id]->getVal('libelle'),
                $this->f->boolean_string_to_boolean($elections_en_cours[$id]->getVal('delegation_participation')) ? null : htmlentities(json_encode($tranches_horaires[$id]), ENT_QUOTES, 'UTF-8'),
                $this->f->boolean_string_to_boolean($elections_en_cours[$id]->getVal('delegation_participation')) ? htmlentities(json_encode($unites_par_election[$id]), ENT_QUOTES, 'UTF-8') : null,
            ),
            array_keys($elections_en_cours)
        );

        echo strtr(<<<'HTML'
            <form id="widget-saisie-participation-form-%widget_id" action="#">
                <div class="field field-type-select">
                    <div class="form-libelle">
                        <label for="widget-saisie-participation-form-%widget_id-election">%election_label</label>
                    </div>
                    <div class="form-content">
                        <select id="widget-saisie-participation-form-%widget_id-election" name="election" required>
                            <option disabled selected>%choisir_placeholder</option>
                            %election_options
                        </select>
                    </div>
                </div>

                <div class="field field-type-select">
                    <div class="form-libelle">
                        <label for="widget-saisie-participation-form-%widget_id-tranche">%tranche_label</label>
                    </div>
                    <div class="form-content">
                        <select id="widget-saisie-participation-form-%widget_id-tranche" name="tranche" required>
                            <option disabled>%choisir_dabord_placeholder</option>
                        </select>
                    </div>
                </div>

                <div class="field field-type-select" style="display: none;">
                    <div class="form-libelle">
                        <label for="widget-saisie-participation-form-%widget_id-unite">%unite_label</label>
                    </div>
                    <div class="form-content">
                        <select id="widget-saisie-participation-form-%widget_id-unite" name="unite">
                            <option disabled>%choisir_dabord_placeholder</option>
                        </select>
                    </div>
                </div>

                <button type="submit">%submit_label</button>
                </form>
                <script>
                    document.addEventListener('DOMContentLoaded', function () {
                        widget_saisie_participation__init_select("#widget-saisie-participation-form-%widget_id");
                    });
                </script>
            HTML,
            [
                '%widget_id' => $widget_id,
                '%election_label' => mb_ucfirst(__('élections')),
                '%election_options' => implode('', $options_elections),
                '%tranche_label' => mb_ucfirst(__('tranche')),
                '%unite_label' => mb_ucfirst(__('unité')),
                '%submit_label' => mb_ucfirst(__('saisir les participations')),
                '%choisir_placeholder' => mb_ucfirst(__('choisir')),
                '%choisir_dabord_placeholder' => mb_ucfirst(__("choisir d'abord une élection")),
            ],
        );

        return false;
    }


    /**
     * Widget de saisie des résultats électoraux.
     *
     * Affichage un sélecteur de l'élection en cours, d'un type de saisie et d'une unité,
     * pour proposer un bouton d'ouverture d'un overlay de saisie des résultats.
     *
     * @param string|null $content Contenu optionnel contenant des paramètres d’affichage : mode_affichage_unites='tableau'|'selection' et identifiant_election=<int>
     * @return bool|string Retourne false si aucun widget à afficher,
     *                     ou le rendu HTML du sélecteur d’élection.
     */
    public function view_widget__saisie_resultat($content = null, int $widget_id = 0)
    {
        $arguments = $this->get_arguments($content, ['mode_affichage_unites', 'identifiant_election']);
        $arguments['mode_affichage_unites'] ??= 'selection';

        $elections_en_cours = $this->get_elections_en_cours($arguments['identifiant_election'] ?? null);
        if (!is_array($elections_en_cours)) {
            echo '<div class="message ui-state-info">', $elections_en_cours, '</div>';
            return false;
        }

        // Pour chaque élection en cours, on va lister les unités avec délégation.
        // La délégation est la même pour les centaines qui dépendent de l'élection, 
        // donc on ne les recalcule pas pour chaque centaine
        $unites_par_election = [];
        foreach ($elections_en_cours as $id => $election) {
            $unites_par_election[$id] = [];
            foreach ($election->get_unites_autorisees_for_user($_SESSION['login'], 'saisie') as $u) {
                array_push($unites_par_election[$id], [
                    'value' => intval($u['unite']),
                    'text' => "{$u['code_unite']} - {$u['libelle']}",
                ]);
            }
        }

        // On récupère les centaines sur les élections
        $elections_tous_ids = array_keys($elections_en_cours);
        $centaines_par_election = [];
        foreach ($elections_en_cours as $id => $election) {
            $centaines_par_election[$id] = [];
            foreach ($election->get_centaines_election() as $c) {
                $centaine_id = $c->getVal($c->clePrimaire);

                array_push($elections_tous_ids, $centaine_id);
                array_push($centaines_par_election[$id], [
                    'value' => intval($centaine_id),
                    'text' => $c->getVal('libelle'),
                ]);
            }
        }

        // Pour l'affichage du lien, on a besoin de l'election_unite et pas juste de l'ID de l'unité
        // Comme cet election_unite dépend du type de saisie (pour les centaines, il change),
        // on fait un tableau de correspondance
        // On en profite pour récupérer l'état de la saisie pour l'affichage en mode tableau
        $sql = sprintf(<<<'SQL'
                SELECT election, unite, election_unite
                FROM %1$selection_unite
                WHERE election IN (%2$s)
            SQL,
            DB_PREFIXE,
            implode(', ', $elections_tous_ids),
            'etat_saisie',
        );
        $res = $this->f->db->query($sql);
        $this->addToLog(__METHOD__ . "(): db->query(\"" . $sql . "\");", VERBOSE_MODE);
        $this->f->isDatabaseError($res);
        /** @var array<string,array<string,string>> les premières clés sont les élections/centaines, les deuxièmes clés les unités */
        $unites_election_mapping = [];
        while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
            $unites_election_mapping[$row['election']][$row['unite']] = $row['election_unite'];
        }

        // -----------------------------------------------------------------------------
        // Affichage

        // On stocke dans chaque option d'élection les unités et les types de saisie disponibles
        $options_elections = array_map(
            fn (int $id) => sprintf(<<<'HTML'
                <option value="%1$d" data-unites='%3$s' data-types='%4$s' data-mapping='%5$s'>%1$d - %2$s</option>
                HTML,
                $id,
                $elections_en_cours[$id]->getVal('libelle'),
                htmlentities(json_encode($unites_par_election[$id]), ENT_QUOTES, 'UTF-8'),
                htmlentities(json_encode(array_merge([
                        // Par défaut, il y a au moins la saisie des résultats globaux
                        ['value' => $id, 'text' => __('Résultats')],
                    ], 
                    $centaines_par_election[$id],
                )), ENT_QUOTES, 'UTF-8'),
                htmlentities(json_encode($unites_election_mapping), ENT_QUOTES, 'UTF-8'),
            ),
            array_keys($elections_en_cours)
        );

        echo strtr(<<<'HTML'
            <form id="widget-saisie-rapide-form-%widget_id" action="#" data-affichage="%mode_affichage_unites">
                <div class="field field-type-select">
                    <div class="form-libelle">
                        <label for="widget-saisie-rapide-form-%widget_id-election">%election_label</label>
                    </div>
                    <div class="form-content">
                        <select id="widget-saisie-rapide-form-%widget_id-election" name="election" required>
                            <option disabled selected>%choisir_placeholder</option>
                            %election_options
                        </select>
                    </div>
                </div>

                <div class="field field-type-select">
                    <div class="form-libelle">
                        <label for="widget-saisie-rapide-form-%widget_id-type">%type_label</label><br>
                    </div>
                    <div class="form-content">
                        <select id="widget-saisie-rapide-form-%widget_id-type" name="type" required>
                            <option disabled>%choisir_dabord_placeholder</option>
                        </select>
                    </div>
                </div>

                <div class="unite-holder">
                    <div class="field field-type-select">
                        <div class="form-libelle">
                            <label for="widget-saisie-rapide-form-%widget_id-unite">%unite_label</label><br>
                        </div>
                        <div class="form-content">
                            <select id="widget-saisie-rapide-form-%widget_id-unite" name="unite" required>
                                <option disabled>%choisir_dabord_placeholder</option>
                            </select>
                        </div>
                    </div>
                </div>

                <button type="submit">%submit_label</button>
                </form>
                <script>
                    document.addEventListener('DOMContentLoaded', function () {
                        widget_saisie_resultat__init("#widget-saisie-rapide-form-%widget_id");
                    });
                </script>
            HTML,
            [
                '%widget_id' => $widget_id,
                '%election_label' => mb_ucfirst(__('élections')),
                '%election_options' => implode('', $options_elections),
                '%type_label' => mb_ucfirst(__('type de saisie')),
                '%type_option_resultat' => mb_ucfirst(__('résultat')),
                '%unite_label' => mb_ucfirst(__('unité')),
                '%submit_label' => mb_ucfirst(__('saisir les résultats')),
                '%choisir_placeholder' => mb_ucfirst(__('choisir')),
                '%choisir_dabord_placeholder' => mb_ucfirst(__("choisir d'abord une élection")),
                '%mode_affichage_unites' => htmlentities($arguments['mode_affichage_unites']),
            ],
        );

        return false;
    }

    // -----------------------------------------------------------------------------
    // -----------------------------------------------------------------------------
    // -----------------------------------------------------------------------------

    /**
     * Vérifie si le portail web est actif.
     * La vérification se fait via une requête sql qui récupère le
     * modèle web actif. Si aucun modèle web n'est récupéré c'est que
     * le portail web n'est pas actif.
     *
     * /!\ cette méthode renvoie false en cas d'erreur de base de données
     *
     * @return boolean
     */
    protected function portail_actif() {
        $sql = sprintf(
            'SELECT
                web
            FROM
                %1$sweb
            WHERE
                actif IS TRUE',
            DB_PREFIXE
        );
        $result = $this->f->get_one_result_from_db_query($sql);
        if ($result['code'] == 'KO') {
            return false;
        }
        // Si le portail web est actif on a un résultat si ce n'est pas le cas
        // la requête ne renverra rien
        return ! empty($result['result']);
    }

    /**
     * Cet méthode permet de formater, la chaîne de caractères reçue du
     * paramétrage du widget en un tableau de valeurs dont les clés
     * correspondent aux clés passées en paramètre.
     *
     * @param string $content
     * @param array $params
     *
     * @return array
     */
    protected function get_arguments($content = null, $params = array()) {
        //
        $arguments = array();
        // On explose les paramètres reçus avec un élément par ligne
        $params_tmp1 = explode("\n", $content);
        // On boucle sur chaque ligne de paramètre
        foreach ($params_tmp1 as $key => $value) {
            // On explose le paramètre de sa valeur avec le séparateur '='
            $params_tmp2[] = explode("=", $value);
        }
        // On boucle sur chaque paramètre reçu pour vérifier si la valeur reçue
        // est acceptable ou non
        foreach ($params_tmp2 as $key => $value) {
            //
            if (!isset($value[0]) || !isset($value[1])) {
                continue;
            }
            //
            if (in_array(trim($value[0]), $params)) {
                $arguments[trim($value[0])] = trim($value[1]);
            }
        }
        //
        return $arguments;
    }

    /**
     * Récupère les élections en cours.
     * Si le paramètre $identifiant_election est passé, limite à l'élection en cours correspondant à l'identifiant
     *
     * @return array<election>|string le tableau des élections ou une chaîne expliquant l'erreur à afficher à l'utilisateur
     */
    protected function get_elections_en_cours(?string $id_election) {
        if (!empty($id_election)) {
            // On utilise l'élection du paramétrage
            $election = $this->f->get_element_by_id(\election::class, $id_election);
            if (!$election->exists()) {
                return sprintf(__('Le paramètre <code>identifiant_election=%s</code> ne correspond pas à aucune élection.'), $id_election);
            }

            if (!($election->is_etape_simulation() || $election->is_etape_saisie())) {
                return sprintf(__('L’élection %s n’est pas en cours.'), $election->getVal('libelle'));
            }

            return [
                $election->getVal($election->clePrimaire) => $election,
            ];
        }

        // On récupère toutes les élections en cours
        $sql = sprintf(<<<'SQL'
            SELECT election
            FROM %1$selection
            WHERE workflow IN ('Simulation', 'Saisie')
                AND is_centaine IS false
            ORDER BY libelle
            SQL,
            DB_PREFIXE,
        );

        $res = $this->f->db->query($sql);
        $this->f->isDatabaseError($res);

        // Oui, on fait des requêtes SQL dans des boucles par la suite,
        // mais on ne devrait pas avoir trop d'élections en cours en même temps
        $elections_en_cours = [];
        while ($r = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
            $id = $r['election'];
            $elections_en_cours[$id] = $this->f->get_element_by_id(\election::class, $id);
        }

        if (0 === count($elections_en_cours)) {
            return __('Aucune élection en cours.');
        }

        return $elections_en_cours;
    }
}
