use strict;
use warnings;

# =============================================================================
# Verificateur du JavaScript du gabarit — EcoVivo, espace client
#
#   perl outils/verifier-js.pl app/vues/gabarit.php   depuis site/
#
# verifier-php.pl est un lexeur PHP : il ne regarde PAS le contenu des blocs
# <script>. Or le gabarit en porte desormais deux cents lignes — envoi
# decoupe, reprise, empreintes. Une accolade oubliee y casse TOUT le
# JavaScript de la page, silencieusement : les tableaux cessent d etre
# cliquables, l envoi decoupe ne demarre plus, et rien ne le dit.
#
# Ce script ne pretend pas analyser du JavaScript. Il verifie ce qu une
# relecture humaine rate le plus souvent : l equilibre des delimiteurs, une
# fois les chaines et les commentaires neutralises, et la presence des
# fonctions attendues.
# =============================================================================
# crochets, et presence des fonctions attendues. Le lexeur PHP du projet ne
# regarde pas le JavaScript ; ce script comble ce seul trou, sans pretendre
# etre un analyseur JS.
my $fichier = shift @ARGV or die "usage: verifier-js.pl <gabarit.php | fichier.js>\n";
open(my $h, '<:encoding(UTF-8)', $fichier) or die "$fichier : $!\n";
my $t = do { local $/; <$h> };
close $h;

# Deux entrees : le bloc <script> d une vue, ou un fichier .js entier.
#
# Le second est venu avec la visite guidee : son moteur vit dans
# public/assets/js/visite.js et non dans le gabarit — celui-ci porte deja six
# cents lignes, et ce script n en lit qu un bloc. Un fichier hors de portee du
# seul verificateur de JavaScript du projet, c est le trou qu il etait censé
# boucher qui se rouvre ailleurs.
my ($js) = $t =~ /<script>(.*?)<\/script>/s;
if (!defined $js) {
    die "bloc <script> introuvable dans $fichier\n" unless $fichier =~ /\.js$/i;
    $js = $t;
}

# Neutralise commentaires puis chaines, pour ne compter que la structure.
$js =~ s{//[^\n]*}{}g;
$js =~ s{/\*.*?\*/}{}gs;
$js =~ s{'(?:\\.|[^'\\])*'}{Q}g;
$js =~ s{"(?:\\.|[^"\\])*"}{Q}g;

my ($acc, $par, $cro) = (0, 0, 0);
for my $c (split //, $js) {
    $acc++ if $c eq '{';  $acc-- if $c eq '}';
    $par++ if $c eq '(';  $par-- if $c eq ')';
    $cro++ if $c eq '[';  $cro-- if $c eq ']';
}
my $erreurs = 0;

printf("  accolades %+d   parentheses %+d   crochets %+d\n", $acc, $par, $cro);
if ($acc == 0 && $par == 0 && $cro == 0) {
    print("  structure equilibree\n");
} else {
    print("  DESEQUILIBRE\n");
    $erreurs++;
}

# Les fonctions ATTENDUES, verifiees une par une. Une seule qui disparait et
# le dispositif qu elle porte cesse de demarrer, sans un mot dans la console.
#
# La liste depend du fichier : le gabarit porte l envoi decoupe, visite.js
# porte la visite guidee et le lexique. Un fichier inconnu n a pas de liste, et
# ne se voit donc controler que sur sa structure — ce qui reste utile.
my %attendues_par_fichier = (
    'gabarit.php' => [qw(envoiBouton envoiBoutonRendre envoiFormulaire envoiFichier
                         envoiDecoupe envoiEmpreinte envoiDejaRecu envoiMessage envoiPoids)],
    'visite.js'   => [qw(dejaVue retenir lireDonnees etapesUtiles construire placer
                         afficher fermer avancer lancer visibleIci legende lexiqueBrancher demarrer)],
);
my ($base) = $fichier =~ m{([^/\\]+)$};
my @attendues = @{ $attendues_par_fichier{$base} // [] };

my @fn = $js =~ /function\s+(\w+)/g;
if (@attendues) {
    printf("  %d fonction(s) definie(s) au total, dont les %d attendues :\n",
           scalar @fn, scalar @attendues);
    for my $f (@attendues) {
        my $la = $js =~ /function\s+\Q$f\E\b/;
        printf("    %-18s %s\n", $f, $la ? 'definie' : 'ABSENTE');
        $erreurs++ unless $la;
    }
} else {
    printf("  %d fonction(s) definie(s) ; aucune liste attendue pour %s\n",
           scalar @fn, $base);
}

# LE VERDICT, comme les deux autres verificateurs du projet.
#
# Il manquait : le script disait « DESEQUILIBRE » ou « ABSENTE » au fil de
# l eau, puis se taisait. On ne savait pas si l on venait de lire un rapport
# sain ou un rapport d echec, et un protocole d essai ne pouvait pas demander
# « zero erreur » puisque le mot n apparaissait nulle part.
#
# Une fonction ABSENTE compte desormais pour une erreur : le script la
# signalait deja, mais rendait quand meme 0 — donc une verification enchainee
# derriere un && passait outre.
printf("\n%d erreur(s).\n", $erreurs);
print($erreurs == 0 ? "OK.\n" : "A CORRIGER.\n");
exit($erreurs == 0 ? 0 : 1);
