use strict; no warnings;

use Plack::Test;
use Test::More tests => 17;
use HTTP::Cookies ();
use HTTP::Request::Common;
use Plack::Middleware::SignedCookies ();

sub ws () { join '', map +(' ',"\t")[rand 2], 0 .. rand 10 }
sub mkck { my $kv = join ws.'='.ws, splice @_, 0, 2; join ';', map ws.$_.ws, $kv, @_ }

my ( $_s, $_h );

my $mw = Plack::Middleware::SignedCookies->new( app => sub {
	my @h = map +( 'Set-Cookie', $_ ), (
		mkck( 'c0' ), # missing equals sign
		mkck( 'cb', 'lorem "ipsum"' ),
		mkck( 'ch', 'dolor sit\\', ('HTTPONLY') x !!$_h ),
		mkck( 'cs', 'amet, consectetur', ('SECURE') x !!$_s ),
		mkck( 'cx', q['adipiscing elit'], ('SECURE') x !!$_s, ('HTTPONLY') x !!$_h ),
	);
	[ 200, \@h, [ join ';', sort split / *; */, $_[0]{'HTTP_COOKIE'}, -1 ] ];
} );

test_psgi app => $mw->to_app, client => sub {
	my ( $cb, $jar, $res, $all, $hto, $sec ) = ( shift, HTTP::Cookies->new );
	my $get = sub {
		my $req = $jar->add_cookie_header( GET 'http://127.0.0.1/' );
		$req->header( cookie => join ';', map mkck( split /=/, $_, 2 ), split /;/, $req->header( 'cookie' ) );
		$jar->extract_cookies( $res = $cb->( $req ) );
		$_ = [] for $all, $hto, $sec;
		$jar->scan( sub {
			push @$all, $_[1];
			push @$sec, $_[1] if $_[7];
			push @$hto, $_[1] if grep 'httponly' eq lc, keys %{ $_[-1] };
		} );
		$_ = join '!', sort @$_ for $all, $hto, $sec;
		$res->content;
	};

	is length, 17, 'Secret has been autogenerated: ' . sprintf '\\x%02X' x length, unpack 'C*', $_ for $mw->secret;
	is $mw->httponly, 1, 'HttpOnly default has been set';

	$jar->set_cookie( 0, ( cb => 1 ), '/', '127.0.0.1' );
	is $get->(), '', 'Unknown cookies ignored in initial request';

	is $all, 'c0!cb!ch!cs!cx', 'Initial response includes exactly the expected cookies';
	is $hto, 'c0!cb!ch!cs!cx', '... with default HttpOnly flag';
	is $sec, '',               '... and default secure flag';

	is $get->(), 'c0=;cb=lorem "ipsum";ch=dolor sit\\;cs=amet, consectetur;cx=\'adipiscing elit\'', 'Own cookies are recognized';

	$jar->set_cookie( 0, ( cx => 'nonsense' ), '/', '127.0.0.1' );
	is $get->(), 'c0=;cb=lorem "ipsum";ch=dolor sit\\;cs=amet, consectetur', 'Tampered cookies are rejected';

	$mw->secure( 1 );
	$get->();
	is $sec, 'c0!cb!ch!cs!cx', 'Setting the secure flag works';
	is $hto, 'c0!cb!ch!cs!cx', '... with default HttpOnly flag included';

	$_s = 1;
	$get->();
	is $sec, 'c0!cb!ch!cs!cx', '... even when it was already set';
	$_s = 0;

	$mw->httponly( 0 );
	$get->();
	is $hto, '',               'Disabling the HttpOnly flag works';
	is $sec, 'c0!cb!ch!cs!cx', '... with the secure flag still set';

	$_h = 1;
	$get->();
	is $hto, 'ch!cx', '... and it respects a pre-existing flag';
	$_h = 0;

	$mw->secure( 0 );
	$get->();
	is "$sec!!$hto", '!!', 'Clearing both flags works';

	$_s = $_h = 1;
	$get->();
	is $hto, 'ch!cx', '... and respects a pre-existing HttpOnly flag';
	is $sec, 'cs!cx', '... as well as a pre-existing secure flag';
};
