#!/usr/bin/perl

use strict;
use warnings;

use feature qw(state);

use English qw(-no_match_vars);
use File::Temp;
use HTML::Tidy5;
use IPC::Run qw(run);
use Test::HTML::Tidy5 qw();
use Test::More;
use Test::Mojo::WithRoles 'SubmitForm';

plan(skip_all => 'live database required') unless
    $ENV{TEST_DB_HOST} &&
    $ENV{TEST_DB_NAME} &&
    $ENV{TEST_DB_TYPE} &&
    $ENV{TEST_DB_USERNAME} &&
    $ENV{TEST_DB_PASSWORD};

plan tests => 4;

sub named_subtest {
    my ($name, $code, @args) = @_;

    my $tb = Test::More->builder();
    return $tb->subtest($name, $code, @args, $name);
}

sub setup {
    my %args = @_;

    my $name = $args{name};
    my $host = $args{host};
    my $type = $args{type};
    my $username = $args{username};
    my $password = $args{password};

    system("mysqladmin --host=$host --user=$username --password=$password --force drop $name >/dev/null");
    system("mysqladmin --host=$host --user=$username --password=$password create $name >/dev/null") == 0
        or die "can't run mysqladmin: $CHILD_ERROR\n";
    system("mysql --host=$host --user=$username --password=$password $name < conf/manager.sql") == 0
        or die "can't run mysql: $CHILD_ERROR\n";

    my $temp_dir = File::Temp->newdir(CLEANUP => $ENV{TEST_DEBUG} ? 0 : 1);
    diag("temp dir: $temp_dir") if $ENV{TEST_DEBUG};

    my $log_file  = sprintf("%s/test.log", $temp_dir);
    my $conf_file = sprintf("%s/test.conf", $temp_dir);

    open(my $handle, '>', $conf_file);
    print {$handle} <<EOF;
[setup]
templates_theme = edugain

[federations]
edugain = t/edugain.xml

[app]
url = https://my.fqdn/accountmanager
support_email = support\@my.fqdn
name = eduGAIN Access Check

[logger]
file = $log_file
level = debug

[mailer]

[idp]

[database]
host     = $host
name     = $name
type     = $type
username = $username
password = $password
EOF
    close($handle);

    $ENV{MOJO_CONFIG} = $conf_file;
    $ENV{ACCOUNTMANAGER_CONFIG} = $conf_file;

    return $temp_dir;
}

sub get_test_object {
    my %args = @_;

    my $app = $args{app} || 'AccountManager::App';

    my $t = Test::Mojo::WithRoles->new($app);
    my $ua = $t->ua();
    $ua->max_redirects(1);
    $ua->on(start => sub {
        my (undef, $tx) = @_;
        $tx->req()->headers()->from_hash($args{user})
            if $args{user} and !$tx->previous();
        $tx->req()->headers()->accept_language($args{language})
            if $args{language};
    });

    $t->app()->log()->info(sprintf('new test: %s', $args{test}))
        if $args{test};

    return $t;
}

sub html_ok {
    my ($res) = @_;

    state $tidy =  HTML::Tidy5->new({
        'drop-empty-elements' => 0
    });
    $tidy->ignore(type => TIDY_INFO);
    $tidy->ignore(text => qr/<img> lacks "alt" attribute/);
    local $ENV{LANG} = 'C';
    Test::HTML::Tidy5::html_tidy_ok($tidy, $res->text(), "HTML content OK");
}

sub diag_file {
    my ($res, $dir) = @_;

    return if !$ENV{TEST_DEBUG};

    state $count = 1;
    my $file = sprintf("%s/test_%i.html", $dir, $count);
    diag("content saved as $file");

    open (my $handle, '>', $file) or die($ERRNO);
    print $handle $res->text();
    close $handle;

    $count++;
}

my $test_dir = setup(
    host     => $ENV{TEST_DB_HOST},
    name     => $ENV{TEST_DB_NAME}, 
    type     => $ENV{TEST_DB_TYPE},
    username => $ENV{TEST_DB_USERNAME},
    password => $ENV{TEST_DB_PASSWORD}
);

named_subtest "index page" => sub {
    my $t = get_test_object(test => $_[0]);

    $t->get_ok('/')
      ->status_is(200)
      ->text_is('html head title' => 'eduGAIN Access Check')
      ->element_exists('a[href=/step1]', 'get started button');

    my $res = $t->tx()->res();
    html_ok($res) or diag_file($res, $test_dir);
};

named_subtest "SP selection page" => sub {
    my $t = get_test_object(test => $_[0]);

    $t->get_ok('/step1')
      ->status_is(200)
      ->text_is('html head title' => 'eduGAIN Access Check', 'expected title')
      ->element_exists('select[id=all][name=all]', 'SP list');

    my $res = $t->tx()->res();
    html_ok($res) or diag_file($res, $test_dir);
};

named_subtest "email selection page, missing entityid parameter" => sub {
    my $t = get_test_object(test => $_[0]);

    $t->get_ok('/step2' => form => {federation => 'edugain'})
      ->status_is(200)
      ->text_is('html head title' => 'eduGAIN Access Check', 'expected title')
      ->content_like(qr/Error:[\n\s]+missing parameter 'entityid'/, 'expected error message');

    my $res = $t->tx()->res();
    html_ok($res) or diag_file($res, $test_dir);
};

named_subtest "email selection page, invalid entityid parameter" => sub {
    my $t = get_test_object(test => $_[0]);

    $t->get_ok('/step2' => form => {federation => 'edugain', entityid => 'foo'})
      ->status_is(200)
      ->text_is('html head title' => 'eduGAIN Access Check', 'expected title')
      ->content_like(qr/Error:[\n\s]+invalid parameter 'entityid'/, 'expected error message');

    my $res = $t->tx()->res();
    html_ok($res) or diag_file($res, $test_dir);
};