use v6.d;
use Test;
use RakuDoc::Processed;
use RakuDoc::PromiseStrings;
use Test::Deeply::Relaxed;

use PrettyDump;
my $register = PCellTracker.new;

plan 17;

my RakuDoc::Processed $rp;
$rp .= new;
is $rp.name, 'Unnamed-source.txt', 'default name with default format';
$rp .=new( :output-format<html> );
is $rp.name, 'Unnamed-source.html', 'default name with new format';

$rp .= new(:source-data(%(:name('test-prs'), :extra<extra>)));
is $rp.source-data<name>, 'test-prs', 'source data ammended';
is $rp.name, 'test-prs.txt', 'output file name created from source name';
is $rp.source-data<extra>, 'extra', 'source data has extra';

my ProcessedState $ps .= new;
my ProcessedState $pt .= new;
my ProcessedState $pu .= new;
my %expected-ind;
$ps.body ~= 'start' ~ PCell.new(:$register, :id('AAA')) ~ PCell.new(:$register, :id('BBB'));
my $payload = '｢some stuff｣';
$register.add-payload( :$payload, :id<AAA> );
like $ps.body.Str, /
    'start'
    .* { $payload }
    'BBB'
    /, 'ProcessState body handles PStr';
$pt.body ~= 'end';
lives-ok { $pu += [+] $ps, $pt }, 'ProcessedState objects can be added';
like $pu.body.Str, /
    'start'
    .* { $payload }
    .+ 'BBB'
    .+ 'end'
    /, 'ProcessedState object body added as expected';
$payload = '｢more things｣';
$register.add-payload( :$payload, :id<BBB> );

like $pu.body.Str, /
    { $payload }
/, 'body PStr BBB expanded after addition';

# renew ps
$ps .= new;
$ps.body ~= 'start' ~ PCell.new(:$register, :id('XXX')) ~ PCell.new(:$register, :id('YYY'));

$ps.toc.append: %(<one two three> Z=> 1 ..*), %(<four five> Z=> 'a' ..*), ;
$pt.toc.append: %(<tone ttwo tthree> Z=> 1 ..*), %(<tfour tfive> Z=> 'a' ..*), ;
$ps.warnings.append: 'warning 1 is about foo', 'warning 2 is about bar';
$pt.warnings.append: 'Error 1 is third', 'Error 2 is fourth', 'final problem';
$pu += [+] $ps, $pt;
subtest {
    my @toc = $pu.toc;
    is @toc.elems, 4, 'elems OK';
    is-deeply @toc[0].keys.sort, <one three two>, 'first elem as expected';
    is-deeply @toc[3].keys.sort, <tfive tfour>, 'fourth elem as expected';
}, 'ProcessedState object toc added as expected';
subtest {
    my @warns = $pu.warnings;
    is @warns.elems, 5, 'No. of warnings correct';
    like @warns.join, /
       'warning 1' .+ 'warning 2' .+ 'Error 1' .+ 'Error 2' .+ 'final'
    /, 'sequence of warnings seems right';
}, 'warnings compiled in correct order';

$ps.index = %(
    syntax => %(:refs([%(:target<one>,:place<Preface>,),]),
        :sub-index( %() ),
    ),
    kind => %(:refs([%(:target<two>,:place<Preface>,),]),
        :sub-index( %() ),
    ),
);
$pt.index = %(
    syntax => %(:refs([%(:target<tone>,:place<Preface>,),]),
        :sub-index( %(
            hierarchy => %(:refs([%(:target<tone>,:place<Preface>,),]),
                :sub-index( %() ),
            ),
        ) ),
    ),
    xxx => %(:refs([%(:target<yyy>,:place<Preface>,),]),
        :sub-index( %() ),
    ),
);
$pu += [+] $ps, $pt;
%expected-ind = %(
    syntax => %(
        :refs([
            %(:target<one>,:place<Preface>,),
            %(:target<tone>,:place<Preface>,),
        ]),
        :sub-index( %(
            hierarchy => %(:refs([%(:target<tone>,:place<Preface>,),]),
                :sub-index( %() ),
            ),
        ) ),
    ),
    kind => %(:refs([%(:target<two>,:place<Preface>,),]),
        :sub-index( %() ),
    ),
    xxx => %(:refs([%(:target<yyy>,:place<Preface>,),]),
        :sub-index( %() ),
    ),
);
is-deeply-relaxed $pu.index, %expected-ind, 'ProcessedState object index added as expected';

$pu.index = %(
	:Tutorial(%(
		:refs([
			%( :place("Preface"), :target("index-entry-Statements") ),
		]),
		:sub-index( %(
			'statement (Basics)' => ( %(
				:refs([
					%( :place("Preface"), :target("index-entry-Statements") ),
				]),
				:sub-index( %() )
			))
		))
	))
);
$ps.index = %(
	:Tutorial(%(
		:refs([
			%( :place("Preface"), :target("index-entry-Lexical_scope") ),
		]),
		:sub-index( %(
			'my (Basics)' => ( %(
				:refs([
					%( :place("Preface"), :target("index-entry-Lexical_scope") ),
				]),
				:sub-index( %() )
			))
		))
	))
);
$pu += $ps;
%expected-ind =
    :Tutorial(
        %(
            :refs([
                %(:place("Preface"), :target("index-entry-Statements")),
                %(:place("Preface"), :target("index-entry-Lexical_scope"))
            ]),
            :sub-index( %(
                "my (Basics)" => %(
                    :refs([
                        %(:place("Preface"), :target("index-entry-Lexical_scope")),
                    ]),
                    :sub-index( %() )
                ),
                "statement (Basics)" => %(
                    :refs([
                        %(:place("Preface"), :target("index-entry-Statements")),
                    ]),
                    :sub-index( %() )
                )
            ) )
        )
    )
;

is-deeply-relaxed $pu.index, %expected-ind, 'Merge two indices with same key name but different targets';

$ps.semantics = %( syntax => ['one',], kind => ['two',]);
$pt.semantics = %( syntax => ['tone',], xxx => ['yyy',]);
$pu += [+] $ps, $pt;
my %expected-semantics = %(
    syntax => ['one', 'tone'],
    kind => ['two'],
    xxx => ['yyy'],
);
is-deeply-relaxed $pu.semantics, %expected-semantics, 'ProcessedState object semantics added as expected';

$payload = '< more things >';
$register.add-payload( :$payload, :id<XXX> );

like $pu.body.Str, /
    [ { $payload } .+? ] ** 3
/, 'body PStr XXX expanded after addition with three occurrences';

$pu .= new;
my ProcessedState @a = $ps, $pt;
lives-ok { $pu = [+] @a }, 'reduction of + over an array lives';
like $pu.body.Str, /
    ^ .+ 'end' ** 4 $
/, 'content of pu body as expected';

done-testing;
