#!perl

use strict;
use warnings;

use Test::More;
use Test::BDD::Cucumber::StepFile;
use Test::BDD::Cucumber::Parser;
use Test::BDD::Cucumber::Model::Scenario;
use Test::BDD::Cucumber::Model::TagSpec;
use Method::Signatures;

my %ordinals = qw/0 first 1 second 2 third 3 fourth 4 fifth/;

Given qr/a scenario tagged with (.+)/, func ($c) {
	my @tags = $1 =~ m/"\@(.+?)"/g;

	# How many scenarios have we created so far?
	my $count = $c->stash->{'scenario'}->{'count'}++;

	# Create a new one
	my $scenario = Test::BDD::Cucumber::Model::Scenario->new({
		name => $ordinals{ $count },
		tags => \@tags
	});

	# Add it to our list
	my $stack = ($c->stash->{'scenario'}->{'scenarios'} ||= []);
	push( @$stack, $scenario );
};

# OK, seriously? I know it's meant to be natural language, and "Oh look,
# business people are programmers now, lol!" but the syntax the step definition
# file uses is insane. Fail. Grrrr...

sub from_tagspec {
	my ( $c, $expr ) = @_;
	my @scenarios = @{ $c->stash->{'scenario'}->{'scenarios'} };
	my @matched = Test::BDD::Cucumber::Model::TagSpec->new({
		tags => $expr
	})->filter( @scenarios );
	$c->stash->{'scenario'}->{'matched'} = \@matched;
}

When qr/^Cucumber executes scenarios (not |)tagged with (both |)"\@([^"]*)"( (and|or|nor) (without |)"\@([^"]*)")?$/, func ($c) {
	my ( $not_flag, $both, $tag1, $two_part, $joiner, $negate_2, $tag2 ) =
		@{ $c->matches };

	# Normalize nor to or
	$joiner = 'or' if $joiner && $joiner eq 'nor';

	# Negate the second tag if required
	$tag2 = [ not => $tag2 ] if $negate_2;

	# If this is two-part, create that inner atom
	my $inner = $two_part ?
		[ $joiner, $tag1, $tag2 ] :
		$tag1;

	# Create the outer part, based on if it's negated
	my $outer = $not_flag ?
		[ and => [ not => $inner ] ] :
		[ and => $inner ];

	from_tagspec( $c, $outer );
};

# Even I, great regex master, wasn't going to tackle this one in the parser
# above
When qr/^Cucumber executes scenarios tagged with "\@([a-z]+)" but not with both "\@([a-z]+)" and "\@([a-z]+)"/, func ($c) {
	from_tagspec( $c, [ and => $1, [ not => [ and => $2, $3 ] ] ] );
};

Then qr/only the (.+) scenario( is|s are) executed/, func ($c) {
	my $demands = $1;
	my @translates_to;

	# Work out which scenarios we're talking about
	if ( $demands eq 'first two' ) {
		@translates_to = qw/first second/;
	} else {
		$demands =~ s/(,|and)/ /g;
		@translates_to = split(/\s+/, $demands);
	}

	# Work out which were executed
	my @executed = map { $_->name } @{ $c->stash->{'scenario'}->{'matched'} };

	is_deeply( \@executed, \@translates_to, "Right scenarios executed" );
};

# This final scenario has been written in a way that is pretty specific to the
# underlying implementation the author wanted. I didn't implement that way, so
# I'm just going to piggy-back on it, and use the way I've implemented feature
# tags...
Given 'a feature tagged with "@foo"', func ($c) {
	my $feature = Test::BDD::Cucumber::Parser->parse_string(<<'HEREDOC'
@foo
Feature: Name
	Scenario: first
	  Given bla
HEREDOC
	);
	$c->stash->{'scenario'}->{'scenarios'} = $feature->scenarios;
};
Given 'a scenario without any tags', func ($c) {1};
Then 'the scenario is executed', func ($c) {
	ok( $c->stash->{'scenario'}->{'matched'}->[0],
		"Found an executed scenario" );
}


