# Tests WebGUI::Asset::Wobject::Survey
#
#

use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/../../lib";
use Test::More;
use Test::Deep;
use Data::Dumper;
use WebGUI::Test;    # Must use this before any other WebGUI modules
use WebGUI::Session;

#----------------------------------------------------------------------------
# Init
my $session = WebGUI::Test->session;

#----------------------------------------------------------------------------
# Tests
plan tests => 47;

#----------------------------------------------------------------------------
# put your tests here

my ($survey);

my $user = WebGUI::User->new( $session, 'new' );
WebGUI::Test->addToCleanup($user);
my $import_node = WebGUI::Asset->getImportNode($session);

# Create a Survey
$survey = $import_node->addChild( { className => 'WebGUI::Asset::Wobject::Survey', } );
my $tag = WebGUI::VersionTag->getWorking($session);
$tag->commit;
$survey = $survey->cloneFromDb;
WebGUI::Test->addToCleanup($survey);
isa_ok($survey, 'WebGUI::Asset::Wobject::Survey');

my $sJSON = $survey->surveyJSON;

# Load bare-bones survey, containing a single section (S0)
$sJSON->update([0], { variable => 'S0' });

# Add 2 questions to S0
$sJSON->newObject([0]);    # S0Q0
$sJSON->update([0,0], { variable => 'S0Q0', questionType => 'Yes/No' });
$sJSON->newObject([0]);    # S0Q1
$sJSON->update([0,1], { variable => 'S0Q1', questionType => 'Yes/No' });

# Add a new section (S1)
$sJSON->newObject([]);     # S1
$sJSON->update([1], { variable => 'S1' });

# Add 2 questions to S1
$sJSON->newObject([1]);    # S1Q0
$sJSON->update([1,0], { variable => 'S1Q0' });
$sJSON->newObject([1]);    # S1Q1
$sJSON->update([1,1], { variable => 'S1Q1' });

$survey->persistSurveyJSON;

# Now start a response as the test user
$session->user( { user => $user } );

my $responseId = $survey->responseId;
{
    my $s = WebGUI::Asset::Wobject::Survey->newByResponseId($session, $responseId);
    is($s->getId, $survey->getId, 'newByResponseId returns same Survey');
}
is($survey->get('maxResponsesPerUser'), 1, 'maxResponsesPerUser defaults to 1');
ok($survey->canTakeSurvey, '..which means user can take survey');
is($survey->get('revisionDate'), $session->db->quickScalar('select revisionDate from Survey_response where Survey_responseId = ?', [$responseId]), 'Current revisionDate used');

####################################################
#
# startDate
#
####################################################

my $startDate = $survey->startDate;
$survey->startDate($startDate + 10);
is($survey->startDate, $startDate + 10, 'startDate get/set');

####################################################
#
# hasTimedOut
#
####################################################

ok(!$survey->hasTimedOut, 'Survey has not timed out');
$survey->update( { timeLimit => 1 });
$survey->startDate($startDate - 100);
ok($survey->hasTimedOut, '..until we set timeLimit and change startDate');

# Complete Survey
$survey->surveyEnd();

# Uncache canTake
delete $survey->{canTake};
delete $survey->{responseId};
ok(!$survey->canTakeSurvey, 'Cannot take survey a second time (maxResponsesPerUser=1)');
cmp_deeply($survey->responseId, undef, '..and similarly cannot get responseId');

# Change maxResponsesPerUser to 2
$survey->update({maxResponsesPerUser => 2});
delete $survey->{canTake};
ok($survey->canTakeSurvey, '..but can take when maxResponsesPerUser increased to 2');
ok($survey->responseId, '..and similarly can get responseId');

# Change maxResponsesPerUser to 0
$survey->update({maxResponsesPerUser => 0});
delete $survey->{canTake};
delete $survey->{responseId};
ok($survey->canTakeSurvey, '..and also when maxResponsesPerUser set to 0 (unlimited)');
ok($survey->responseId, '..(and similarly for responseId)');

# Start a new response as another user
$survey->update({maxResponsesPerUser => 1});
is($survey->takenCount( { userId => 1 } ), 0, 'Visitor has no responses');
my $u = WebGUI::User->new( $session, 'new' );
WebGUI::Test->addToCleanup($u);
is($survey->takenCount( { userId => $u->userId } ), 0, 'New user has no responses');
delete $survey->{canTake};
delete $survey->{responseId};
$session->user( { userId => $u->userId } );
ok($survey->canTakeSurvey, 'Separate counts for separate users');
ok($survey->responseId, '..(and similarly for responseId)');
# Put things back to normal..
delete $survey->{canTake};
delete $survey->{responseId};
$session->user( { user => $user } );

# Restart the survey
$survey->update({maxResponsesPerUser => 0});
$survey->submitQuestions({
    '0-0-0'        => 'this text ignored',
    '0-1-0'        => 'this text ignored',
});

cmp_deeply(
    $survey->responseJSON->responses,
    superhashof(
        {   '0-1-0' => {
                'time'     => num( time, 5 ),
                'value'    => 1
            },
            '0-0-0' => {
                'time'     => num( time, 5 ),
                'value'    => 1
            },
        }
    ),
    'submitQuestions does the right thing'
);

# Test Restart
$survey->surveyEnd( { restart => 1 } );
cmp_deeply($survey->responseJSON->responses, {}, 'restart removes the in-progress response');
ok($responseId ne $survey->responseId, '..and uses a new responseId');

# Test out exitUrl with an explicit url
use JSON;
my $surveyEnd = $survey->surveyEnd( { exitUrl => 'home' } );
cmp_deeply(from_json($surveyEnd), { type => 'forward', url => '/home' }, 'exitUrl works (it adds a slash for us)');

# Test out exitUrl using survey instance exitURL property
$survey->update({ exitURL => 'getting_started'});
$surveyEnd = $survey->surveyEnd( { exitUrl => undef } );
cmp_deeply(from_json($surveyEnd), { type => 'forward', url => '/getting_started' }, 'exitUrl works (it adds a slash for us)');

# www_jumpTo
{
    # Check a simple www_jumpTo request
    $session->user( { userId => 3 } );
    WebGUI::Test->getPage( $survey, 'www_jumpTo', { formParams => {id => '0'} } );
    is( $session->http->getStatus, '201', 'Page request ok' ); # why is "201 - created" status used??
    is($survey->responseJSON->nextResponse, 0, 'S0 is the first response');

    tie my %expectedSurveyOrder, 'Tie::IxHash';
    %expectedSurveyOrder =  (
        'undefined' => 0,
        '0' => 0,
        '0-0' => 0,
        '0-1' => 1,
        '1' => 2,
        '1-0' => 2,
        '1-1' => 3,
    );
    while (my ($id, $index) = each %expectedSurveyOrder) {
        WebGUI::Test->getPage( $survey, 'www_jumpTo', { formParams => {id => $id} } );
        is($survey->responseJSON->nextResponse, $index, "jumpTo($id) sets nextResponse to $index");
    }
}

# Response Revisioning
{
    # Delete existing responses
    $session->db->write('delete from Survey_response where assetId = ?', [$survey->getId]);
    delete $survey->{responseId};
    delete $survey->{surveyJSON};

    my $surveyId = $survey->getId;
    my $revisionDate = WebGUI::Asset->getCurrentRevisionDate($session, $surveyId);
    ok($revisionDate, 'Revision Date initially defined');

    # Modify Survey structure, new revision not created
    $survey->submitObjectEdit({ id =>  "0", text => "new text"});
    is($survey->surveyJSON->section([0])->{text}, 'new text', 'Survey updated');
    is($session->db->quickScalar('select revisionDate from Survey where assetId = ?', [$surveyId]), $revisionDate, 'Revision unchanged');

    # Push revisionDate into the past because we can't have 2 revision dates with the same epoch (this is very hacky)
    $revisionDate--;
    $session->stow->deleteAll();
    WebGUI::Cache->new($session)->flush;
    $session->db->write('update Survey set revisionDate = ? where assetId = ?', [$revisionDate, $surveyId]);
    $session->db->write('update assetData set revisionDate = ? where assetId = ?', [$revisionDate, $surveyId]);
    $session->db->write('update wobject set revisionDate = ? where assetId = ?', [$revisionDate, $surveyId]);

    $survey = WebGUI::Asset->new($session, $surveyId);
    isa_ok($survey, 'WebGUI::Asset::Wobject::Survey', 'Got back survey after monkeying with revisionDate');
    is($session->db->quickScalar('select revisionDate from Survey where assetId = ?', [$surveyId]), $revisionDate, 'Revision date pushed back');

    # Create new response
    my $responseId = $survey->responseId;
    is(
        $session->db->quickScalar('select revisionDate from Survey_response where Survey_responseId = ?', [$responseId]), 
        $revisionDate, 
        'Pushed back revisionDate used for new response'
    );

    # Make another change, causing new revision to be automatically created
    $survey->submitObjectEdit({ id =>  "0", text => "newer text"});

    my $newerSurvey = WebGUI::Asset->new($session, $surveyId); # retrieve newer revision
    isa_ok($newerSurvey, 'WebGUI::Asset::Wobject::Survey', 'After change, re-retrieved Survey instance');
    is($newerSurvey->getId, $surveyId, '..which is the same survey');
    is($newerSurvey->surveyJSON->section([0])->{text}, 'newer text', '..with updated text');
    ok($newerSurvey->get('revisionDate') > $revisionDate, '..and newer revisionDate');

    # Create another response (this one will use the new revision)
    my $newUser = WebGUI::User->new( $session, 'new' );
    WebGUI::Test->addToCleanup($newUser);
    $session->user({ user => $newUser });
    my $newResponseId = $survey->responseId;
    is($newerSurvey->responseJSON->nextResponseSection()->{text}, 'newer text', 'New response uses the new text');

    # And the punch line..
    is($survey->responseJSON->nextResponseSection()->{text}, 'new text', '..wheras the original response uses the original text');

}

# Test visualization
eval 'use GraphViz';

SKIP: {

skip "Unable to load GraphViz", 1 if $@;

$survey->surveyJSON->remove([1]);
my ($storage, $filename) = $survey->graph( { format => 'plain', layout => 'dot' } );
like($storage->getFileContentsAsScalar($filename), qr{
    ^graph .*       # starts with graph
    (node .*){3}    # ..then 3 nodes
    (edge .*){3}    # ..then 3 edges
    stop$            # ..and end with stop
}xs, 'Generated graph looks roughly okay');

}

$survey->getAdminConsole();
my $adminConsole = $survey->getAdminConsole();
cmp_deeply(
    $adminConsole->{_submenuItem},
    [
        {
          'extras' => undef,
          'url' => re('func=edit$'),
          'label' => 'Edit'
        },
        {
          'extras' => undef,
          'url' => re('func=editSurvey$'),
          'label' => 'Edit Survey'
        },
        {
          'extras' => undef,
          'url' => re('func=takeSurvey$'),
          'label' => 'Take Survey'
        },
        {
          'extras' => undef,
          'url' => re('func=graph$'),
          'label' => 'Visualize'
        },
        {
          'extras' => undef,
          'url' => re('func=editTestSuite$'),
          'label' => 'Test Suite'
        },
        {
          'extras' => undef,
          'url' => re('func=runTests$'),
          'label' => 'Run All Tests'
        },
        {
          'extras' => undef,
          'url' => re('func=runTests;format=tap$'),
          'label' => 'Run All Tests (TAP)'
        }
    ],
    "Admin console submenu",
);

####################################################
#
# www_loadSurvey
#
####################################################

my $survey_json = $survey->www_loadSurvey({});
my $survey_data = JSON::from_json($survey_json);
unlike($survey_data->{edithtml}, qr/\^International/, 'www_loadSurvey process macros');
