use strict;
use warnings;
use Test::More;
use HTML::FormHandler::Test;

{
    package Test::XSSForm;
    use HTML::FormHandler::Moose;
    extends 'HTML::FormHandler';

    # documented apply-action idioms -- nothing custom
    has_field 'code' => ( type => 'Text', apply => [ { check => qr/^\d+$/ } ] );
    has_field 'plan' => ( type => 'Text', apply => [ { check => [ 'basic', 'pro' ] } ] );
    has_field 'age'  => ( type => 'Text', apply => [ { type  => 'Int' } ] );
    has_field 'color' =>
        ( type => 'Select', options => [ { value => 'red', label => 'Red' } ] );

    no HTML::FormHandler::Moose;
}

my $PAYLOAD = q{<img src=x onerror=alert(1)>};
my $ESCAPED = q{&lt;img src=x onerror=alert(1)&gt;};

my $benign = Test::XSSForm->new;
$benign->process( params => { code => '123', plan => 'basic', age => '42', color => 'red' } );
my $benign_html = $benign->render;

ok($benign->validated,
    "control 3: benign submission did not validate");
is(index($benign_html, $PAYLOAD), -1,
    "control 3: payload present without submitting it");
is(index($benign_html, $ESCAPED), -1,
    "control 3: payload present without submitting it");

my $form = Test::XSSForm->new;
$form->process(
    params => {
        code  => $PAYLOAD,
        plan  => $PAYLOAD,
        age   => $PAYLOAD,
        color => $PAYLOAD,
    }
);

diag "per-field error messages:";
my %err;
for my $field ($form->fields) {
    my $msg = join ' | ', $field->all_errors;
    $err{$field->name} = $msg;
    my $field_html = $field->render;
    # diag sprintf "  %-6s %s %s\n", $field->name . ':', $field_html, $msg;
    is(index($field_html, 'value="' . $PAYLOAD), -1,
        "field '" . $field->name . "' render html value doesn't contain unescaped value");
    ok(index($field_html, '<span class="error_message">') > 0,
        "field '" . $field->name . "' render html contains error message");
    is(index($field_html, '<span class="error_message">' . $PAYLOAD), -1,
        "field '" . $field->name . "' render html error message doesn't contain unescaped value");
}

# form rendering
my $html = $form->render;

my $raw_count = () = $html =~ /\Q$PAYLOAD\E/g;
is($raw_count, 0,
    "no unescaped payload in the rendered form output");

my $escaped_count = () = $html =~ /\Q$ESCAPED\E/g;
# 3x value + 4x error message
is($escaped_count, 7,
    "four escaped payloads in the rendered form output");

my @raw_spans = $html =~ m{<span class="error_message">([^<]*(?:<(?!/span>)[^<]*)*)</span>}g;
my @tainted = grep { index($_, $PAYLOAD) >= 0 } @raw_spans;
is(scalar @tainted, 0,
    "no error spans contain unescaped markup");

done_testing;
