The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!perl
use warnings; use strict;
use Test::More;
use Test::Fatal;

use File::Spec;
use English qw(-no_match_vars);
use version;

use lib '.';
use t::Elive;

use Elive;
use Elive::View::Session;
use Elive::Entity::Session;
use Elive::Entity::User;
use Elive::Entity::Group;

eval "use Test::Script::Run 0.04 qw{:all}";

if ( $EVAL_ERROR ) {
    my $msg = 'Test::Script::Run 0.04+ required to run scripts';
    plan( skip_all => $msg );
}

plan(tests => 200);

our $t = Test::More->builder;

local ($ENV{TERM}) = 'dumb';

my $script_name = 'elive_raise_meeting';

#
# try running script with --help
#

do {
    my ( $return, $stdout, $stderr ) = run_script($script_name, ['--help'] );
    my $status = last_script_exit_code();
    is($status   => 0, "$script_name --help: zero exit status");
    if (Elive->debug) {
	$t->skip('debugging enabled - wont check stderr');
    }
    else {
	is($stderr   => '', "$script_name describe user: no errors");
    }

    like($stdout => qr{usage:}ix, "$script_name --help: stdout =~ 'usage:...''");
};

#
# try with invalid option
#

do {
    my ( $return, $stdout, $stderr ) = run_script($script_name, ['--invalid-opt']);
    my $status = last_script_exit_code();

    isnt($status => 0, "$script_name invalid option: non-zero exit status");
    is($stdout   => '', "$script_name invalid option: stdout empty");
    like($stderr => qr{unknown \s+ option}ix, "$script_name invalid option: message");
    like($stderr => qr{usage:}ix, "$script_name invalid option: usage");

};

SKIP: {

    my %result = t::Elive->test_connection(only => 'real');
    my $auth = $result{auth};

    skip ($result{reason} || 'skipping live tests', 193)
	unless $auth && @$auth && $auth->[0] && $auth->[1] && $auth->[2];

    my $meeting_name = 'test meeting, generated by t/script-elive_raise_meeting.t';

    my $meeting_response_re = qr{^created meeting: (.*?) with id (\d+)$}im;

    my $connection_class = $result{class};
    my $connection = $connection_class->connect(@$auth)
	or die "failed to connect?";

    Elive->connection( $connection );

    my $min_elm3_version =  '9.5.0';
    my $server_details = $connection->server_details;
    my $server_version = $server_details->version;

    my $have_elm3 = do {
	my $min_elm3_version_num = version->new($min_elm3_version)->numify;
	my $server_version_num = version->new($server_version)->numify;

	$server_version_num >= $min_elm3_version_num;
    };

    my $preload = Elive::Entity::Preload->upload(
	{
	    type => 'whiteboard',
	    name => 'test.wbd',
	    ownerId => $connection->login,
	    data => 'junkity junk junk',
	}, connection => $connection);

    foreach my $class (qw{Elive::View::Session Elive::Entity::Session}) {

	if ($class eq 'Elive::Entity::Session' && ! $have_elm3) {
	    diag "*** Skipping class $class for Elluminate Live! version $server_version < $min_elm3_version";
	    $t->skip("$class for Elluminate Live! version $server_version < $min_elm3_version")
		for (1..95);
	    next;
	}

	note "*** Testing class: $class";
	my ($participant1, $participant2, $moderator1);

	is( exception {
	    #
	    # for datasets with 1000s of entries
	    ($participant1, $participant2, $moderator1) = grep {$_->loginName ne $auth->[1]} @{ Elive::Entity::User->list(filter => 'lastName = Sm*') };
	    # for middle sized datasets
	    ($participant1, $participant2, $moderator1) = grep {$_->loginName ne $auth->[1]} @{ Elive::Entity::User->list(filter => 'lastName = S*') }
	    unless $moderator1;
	    #
	    # for modest test datasets
	    ($participant1, $participant2, $moderator1) = grep {$_->loginName ne $auth->[1]} @{ Elive::Entity::User->list() }
	    unless $moderator1;
	    } => undef,
	    'get_users - lives');

	my @meeting_args = (
	    $auth->[0],
	    -user => $auth->[1], -pass => $auth->[2],
	    -name => $meeting_name,
	    -use  => $class,
	    );

	# elive_raise_meeting should accept loginName or userId 
	push(@meeting_args, -participants => $participant1->loginName)
	if $participant1;

	push(@meeting_args, $participant2->userId) if $participant2;

	my $participant_group;

	if ($class eq 'Elive::Entity::Session') {
	    # elm3 specific options
	    push (@meeting_args, 'Robert(bob@example.com)');

	    ($participant_group) = grep {$_->members && @{ $_->members}} @{ Elive::Entity::Group->list() };
	    push(@meeting_args, '*'. $participant_group->groupId)
		if $participant_group;
	}

	push(@meeting_args, -moderators => $moderator1->loginName)
	    if $moderator1;

      MEETING_DEFAULTS:
	do {
	    my $time = time();
	    note "$class creating meeting: @meeting_args";
	    my ( $return, $stdout, $stderr ) = run_script($script_name, \@meeting_args );
	    my $status = last_script_exit_code();

	    is( $status  => 0, "$script_name: zero exit status");

	    if (Elive->debug) {
		$t->skip('debugging enabled - wont check stderr');
	    }
	    else {
		is($stderr   => '', "$script_name - stderr empty");
	    }

	    like($stdout => $meeting_response_re, 'meeting response');
	    my ($ret_meeting_name, $ret_meeting_id) = ($stdout =~ $meeting_response_re);

	    ok($ret_meeting_name, "meeting name returned");
	    ok($ret_meeting_id, "meeting id returned");

	    unless ($ret_meeting_name && $ret_meeting_id) {
		die "unable to raise simple meeting - aborting";
	    }

	    is($ret_meeting_name => $meeting_name, 'echoed meeting name as expected');

	    my $meeting;
	    ok($meeting = $class->retrieve($ret_meeting_id, connection => $connection), 'meeting retrieve');

	    unless ($meeting) {
		die "unable to retrieve meeting: $ret_meeting_id - aborting";
	    }

	    is($meeting->name => $meeting_name, 'retrieved meeting name as expected');

	    my $start = substr($meeting->start, 0, -3) + 0;
	    my $end = substr($meeting->end, 0, -3) + 0;
	    my $duration = $end - $start;

	    ok ($start >= $time - 60 && $start <= $time + 16*60, "default start time within about 15 mins");

	    ok($duration >= 9 * 60 && $duration <= 61 * 60 , "sensible default duration");

	    my $participant_list;
	    ok($participant_list = $meeting->participantList, 'participants retrieve')
		 or die "unable to continue without a participant list";

	    my @participants = @{ $participant_list->participants || {} };

	    is (scalar @participants,
		1  # facilitator
		+ ($class eq 'Elive::Entity::Session'? 1: 0) # invited guest - elm3 only
		+ ($participant_group?1:0)
		+ ($participant1?1:0) + ($participant2?1:0) + ($moderator1?1:0),
		"Meeting has expected number of participants");

	    if ($participant1) {
		my ($found_participant) = grep{$_->user && $_->user->userId eq $participant1->userId} @participants;
		ok($found_participant, '"-participants" option; user added');  
		ok($found_participant && ! $found_participant->is_moderator, '"-participant" option; participant has expected role')
		    or diag "unexpected moderator role for: " . $participant1->userId;
	    }
	    else {
		$t->skip("Not enough users to run this test")
		    for (1..2);
	    }

	    if ($participant2) {
		my ($found_participant) = grep{$_->user && $_->user->userId eq $participant2->userId} @participants;
		ok($found_participant, '"-participants" option; user added');  
		ok($found_participant && ! $found_participant->is_moderator, '"-participant" option; participant has expected role')
		    or diag "unexpected moderator role for: " . $participant2->userId;

	    }
	    else {
		$t->skip("Not enough users to run this test")
		    for (1..2);
	    }

	    if ($moderator1) {
		my ($found_moderator) = grep{$_->user && $_->user->userId eq $moderator1->userId} @participants;
		ok($found_moderator, '"-moderators" option; user added');  
		ok($found_moderator && $found_moderator->is_moderator, '"-moderator" option; moderator has expected role')
		    or diag "didn't have expected moderator role: " . $moderator1->userId;

	    }
	    else {
		$t->skip("Not enough users to run this test")
		    for (1..2);
	    }

	    if ($class eq 'Elive::Entity::Session') {
		my ($found_guest) = grep{$_->guest} @participants;

		ok($found_guest, '"-moderators" option; guest added');  

		is($found_guest && $found_guest->guest->displayName, 'Robert',
		   '"guest display name - as expected'); 

		is($found_guest && $found_guest->guest->loginName, 'bob@example.com',
		   '"guest login name - as expected'); 

		if ($participant_group) {
		    my ($found_participant) = grep{$_->group && $_->group->groupId eq $participant_group->groupId} @participants;
		    ok($found_participant, '"-participants" option; group added');  
		    ok($found_participant && ! $found_participant->is_moderator, '"-participant" option; group has expected role'); 
		}
		else {
		    $t->skip("Not enough groups to run this test")
			for (1..2);
		}

	    };

	    is( exception {$meeting->delete} => undef, 'deletion - lives');
	};

      REPEATED_MEETING:
	do {
	    note ("\t-- Testing Basic Options");
	    my $weeks = 1;
	    my $time = time();
	    my $start_time = $time + 3600;
	    my $end_time = $start_time + 1800;
	    my @start = localtime($start_time);
	    my @end   = localtime($end_time);
	    my $cost_center = 'test-cost-center';
	    my $user_notes = 'test-user-notes';
	    my $moderator_notes = 'test-moderator-notes';
	    my $exit_url;
	    my $preload_id = $preload->preloadId;
	    my $invited_guest = 'Robert(bob@test.org)';

	    my $start_str = sprintf("%04d-%02d-%02d %02d:%02d", $start[5]+1900, $start[4]+1, $start[3], $start[2], $start[1]);
	    my $end_str = sprintf("%04d-%02d-%02d %02d:%02d", $end[5]+1900, $end[4]+1, $end[3], $end[2], $end[1]);
	    my $meeting_pass = "test-".t::Elive::generate_id();

	    my %basic_meeting_args = (
		-start => $start_str,
		-end   => $end_str,
		-meeting_pass => $meeting_pass,
		-cost_center  => $cost_center,
		-user_notes   => $user_notes,
		-moderator_notes  => $moderator_notes,
		-add_preload => $preload_id,
		);

	    if ($class eq 'Elive::View::Session') {
		$weeks = 3;
		$basic_meeting_args{-occurs} = "weeks=$weeks";
	    }
	    elsif ($class eq 'Elive::Entity::Session') {
		$exit_url = 'http://perlmonks.org';
		$basic_meeting_args{-exit_url} = $exit_url;
		note "$class - cant (yet) do repeating meetings";
	    }
	    else {
		die "unknown class: $class";
	    }

	    my ( $return, $stdout, $stderr ) = run_script($script_name,
							  [@meeting_args,
							   %basic_meeting_args,
							   $invited_guest,
							  ] );

	    if ($class eq 'Elive::Entity::Session') {
		if (Elive->debug) {
		    $t->skip('debugging enabled - wont check stderr');
		}
		else {
		    is($stderr   => '', "$script_name - stderr empty ($class)");
		}
	    }
	    else {
		like($stderr => qr{ignoring invited guests}, "std has 'ignoring invitied guests' warning");
	    }

	    my $last_meeting_start;
	    my $week;

	    my $resp = $stdout;
	    #
	    # meeting passwords are not returned directly. Instead we search
	    # for meetings that contain the password.
	    #
	    my $meetings_with_this_password = Elive::Entity::Meeting->list(filter => "password = ". Elive::Entity::Meeting->quote($meeting_pass), connection => $connection);

	    while ($resp =~ s{$meeting_response_re}{}) {
		$week++;
		my $ret_meeting_name = $1;
		my $ret_meeting_id   = $2;

		ok($ret_meeting_name, "week $week: meeting name returned");
		ok($ret_meeting_id, "week $week: meeting id returned");

		is($ret_meeting_name => $meeting_name, "week $week: echoed meeting name as expected");

		my $meeting;
		ok($meeting = $class->retrieve($ret_meeting_id, connection => $connection), "week $week: retrieve");

		unless ($meeting) {
		    die "unable to retrieve meeting: $ret_meeting_id - aborting";
		}

		if ($week == 1) {
		    my $actual_start_time = substr($meeting->start, 0, -3);
		    my $actual_end_time = substr($meeting->end, 0, -3);

		    ok(abs($actual_start_time - $start_time) <= 120, "week $week: actual start time as expected");	
		    ok(abs($actual_end_time - $end_time) <= 120, "week $week: actual end time as expected");
		}

		is($meeting->cost_center => $cost_center, "week $week: cost_center as expected");
		is($meeting->user_notes => $user_notes, "week $week: user_notes as expected");
		is($meeting->moderator_notes => $moderator_notes, "week $week: moderator_notes as expected");

		is($meeting->name => $meeting_name, "week $week: retrieved meeting name as expected");

		is($meeting->redirectURL => $exit_url, "week $week: redirectURL as expected")
		    if $exit_url;

		my $meeting_preloads = $meeting->list_preloads;
		is_deeply([map {$_->preloadId} @$meeting_preloads] => [$preload_id], "Week $week - preload added");

		ok(do {grep {$_->meetingId eq $ret_meeting_id} @$meetings_with_this_password}, "week $week: meeting password as expected");

		my $start = substr($meeting->start, 0 , -3);

		if ($last_meeting_start) {
		    ok(t::Elive::a_week_between($last_meeting_start, $start), sprintf('weeks %d - %d: meetings separated by a week (approx)', $week-1,$week));
		}

		$last_meeting_start = $start;

		is( exception {$meeting->delete} => undef, "week $week: deletion - lives");
	    }

	    ok ($week == $weeks, "expected number of meeting repeats ($weeks)");
	};

	my @flags = qw(invites private permissions raise_hands supervised);

	push (@flags, qw{restricted follow_moderator all_moderators})
	    if $class eq 'Elive::Entity::Session';

      FLAGS_ON:
	do {
	    note ("\t-- Testing Flags On");

	    my @flags_on_args = (
		map {'-'.$_} @flags
		);

	    my ( $return, $stdout, $stderr ) = run_script($script_name,
							  [@meeting_args,
							   @flags_on_args
							  ] );

	    if (Elive->debug) {
		$t->skip('debugging enabled - wont check stderr');
	    }
	    else {
		is($stderr   => '', "$script_name - stderr empty");
	    }
	    like($stdout => $meeting_response_re, 'meeting response');
	    my ($ret_meeting_name, $ret_meeting_id) = ($stdout =~ $meeting_response_re);

	    my $meeting;
	    ok($meeting = $class->retrieve($ret_meeting_id, connection => $connection), "meeting retrieve");

	    unless ($meeting) {
		die "unable to retrieve meeting: $ret_meeting_id - aborting";
	    }

	    foreach my $flag (@flags) {
		my $result = $meeting->$flag;

		ok($result, "meeting -${flag} as expected");
	    }

	    is( exception {$meeting->delete} => undef, "deletion - lives");

	};

      FLAGS_OFF:
	do {
	    note ("\t-- Testing Flags Off");

	    my @flags_off_args = (
		map {'-no'.$_} @flags
		);

	    my ( $return, $stdout, $stderr ) = run_script($script_name,
							  [@meeting_args,
							   @flags_off_args
							  ] );

	    if (Elive->debug) {
		$t->skip('debugging enabled - wont check stderr');
	    }
	    else {
		is($stderr   => '', "$script_name - stderr empty");
	    }

	    like($stdout => $meeting_response_re, 'meeting response');
	    my ($ret_meeting_name, $ret_meeting_id) = ($stdout =~ $meeting_response_re);

	    my $meeting;
	    ok($meeting = $class->retrieve($ret_meeting_id, connection => $connection), "meeting retrieve");

	    unless ($meeting) {
		die "unable to retrieve meeting: $ret_meeting_id - aborting";
	    }

	    foreach my $flag (@flags) {
		my $result = $meeting->$flag;

		ok(!$result, "meeting -no${flag} as expected");
	    }

	    is( exception {$meeting->delete} => undef, "deletion - lives");

	};

      OPTIONS:
	do {
	    note ("\t-- Testing Options");

	    my %option_values = (
		boundary => [0, 15, 30],
		max_talkers => [0, 1, 4],
		recording => [qw{on off remote}],
		);

	    if ($class eq 'Elive::Entity::Session') {
		# elm3 only
		$option_values{max_cameras} = [3, 2, 1];
		$option_values{recording_resolution} = [qw(CG MG FC)];
		$option_values{profile} = [qw(none mod all)];
	    }	    

	    foreach my $run (0,1,2) {

		my @options;

		foreach my $option (sort keys %option_values) {
		    push (@options, '-'.$option => $option_values{$option}[$run]);
		}

		my ( $return, $stdout, $stderr ) = run_script($script_name,
							      [@meeting_args,
							       @options,
							      ] );

		if (Elive->debug) {
		    $t->skip('debugging enabled - wont check stderr');
		}
		else {
		    is($stderr   => '', "$script_name - stderr empty");
		}
		like($stdout => $meeting_response_re, 'meeting response');
		my ($ret_meeting_name, $ret_meeting_id) = ($stdout =~ $meeting_response_re);

		my $meeting;
		ok($meeting = $class->retrieve($ret_meeting_id, connection => $connection), "meeting retrieve");

		unless ($meeting) {
		    die "unable to retrieve meeting: $ret_meeting_id - aborting";
		}

		foreach my $option (sort keys %option_values) {

		    my $expected_value =  $option_values{$option}[$run];
		    my $result = $meeting->$option;
		    
		    is($result => $expected_value, "meeting run $run: -${option}, $expected_value");
		}

		is( exception {$meeting->delete} => undef, "deletion - lives");

	    }
	}
    }

    $preload->delete;
}

Elive->disconnect;