use v5.40.0;
use common::sense;
use feature 'signatures';

use Test::More;
use File::Basename qw(dirname);
use File::Find     qw(find);
use File::Spec;
use File::Temp qw(tempdir);
use lib 'lib';

use Mojo::PrettyTidy;

eval { require Mojolicious; 1 }
    or plan skip_all =>
    'Mojolicious is required for bundled sample template tests';

my $mojo_root =
    File::Spec->catdir( dirname( $INC{'Mojolicious.pm'} ), 'Mojolicious' );

-d $mojo_root
    or plan skip_all => "Could not find Mojolicious resource directory";

my @samples;

find(
  {
   wanted => sub {
     return unless -f $_;
     return unless /\.html\.ep\z/;
     push @samples, $File::Find::name;
   },
   no_chdir => 1,
  },
  $mojo_root, );

@samples
    or plan skip_all => 'No Mojolicious .html.ep templates found';

my $pt     = Mojo::PrettyTidy->new;
my $tmpdir = tempdir( CLEANUP => 1 );

sub _without_js_reformat_note ( $text ) {
  my $note = Mojo::PrettyTidy->_js_prebake_inject;

  my @lines = split /\n/, $note;
  @lines = grep {length} @lines;

  my $pattern = join "\n", map { '[ \t]*' . quotemeta( $_ ) } @lines;

  $text =~ s/$pattern\n*//g;

  return $text;
}

sub ep_tag_kinds ( $text ) {
  my @tags = $text =~ /<%(?:==|=|#)?[\s\S]*?%>/g;

  return [
    map {
      /^<%(==|=|#)?/;
      defined $1 ? $1 : '';
    } @tags ];
}

sub first_diff_line ( $left, $right ) {
  my @left  = split /\n/, $left,  -1;
  my @right = split /\n/, $right, -1;

  my $max = @left > @right ? @left : @right;

  for my $i ( 0 .. $max - 1 ) {
    next
        if defined $left[$i]
        && defined $right[$i]
        && $left[$i] eq $right[$i];

    return {
            line  => $i + 1,
            left  => defined $left[$i]  ? $left[$i]  : '<missing>',
            right => defined $right[$i] ? $right[$i] : '<missing>',};
  }

  return;
}

sub slurp ( $path ) {
  open my $fh, '<:encoding(UTF-8)', $path
      or die "Cannot read $path: $!";
  local $/;
  return <$fh>;
}

sub spurt ( $path, $text ) {
  open my $fh, '>:encoding(UTF-8)', $path
      or die "Cannot write $path: $!";
  print {$fh} $text;
  return;
}

for my $source ( @samples ) {
  my $name = $source;
  $name =~ s{\A\Q$mojo_root\E/?}{};

  subtest "Mojolicious sample: $name" => sub {
    my $input = slurp( $source );

    my $copy = File::Spec->catfile( $tmpdir, $name );
    my $dir  = dirname( $copy );

    require File::Path;
    File::Path::make_path( $dir );

    spurt( $copy, $input );

    my $output = $pt->tidy( $input );

   # The JS warning note is generated by the formatter itself. Strip it before
   # second-pass input/comparison so this smoke test checks formatter stability,
   # not whether the warning comment gets reformatted as JavaScript.
    my $second_input = _without_js_reformat_note( $output );
    my $again        = $pt->tidy( $second_input );

    my $output_cmp = _without_js_reformat_note( $output );
    my $again_cmp  = _without_js_reformat_note( $again );

    if ( $again_cmp ne $output_cmp ) {
      my $diff = first_diff_line( $output_cmp, $again_cmp );

      diag "non-idempotent Mojolicious template: $name";
      diag "source path: $source";
      diag "temp copy:   $copy";

      if ( $diff ) {
        diag "first differing formatted-output line: $diff->{line}";
        diag "first pass : $diff->{left}";
        diag "second pass: $diff->{right}";
      }
    }

    is( $again_cmp, $output_cmp,
        "formatted output is stable for $name ignoring JS reformat note", );
  }
}

done_testing;
