use Mojo::Base -strict;

use Test::More;
use Mojo::Util qw(decode gunzip);
use Mojolicious;

# Partial rendering
my $app = Mojolicious->new(secrets => ['works']);
my $c   = $app->build_controller;
$c->app->log->level('debug')->unsubscribe('message');
is $c->render_to_string(text => 'works'), 'works', 'renderer is working';

# Normal rendering with default format
my $renderer = $c->app->renderer->default_format('test');
$renderer->add_handler(
  debug => sub {
    my ($renderer, $c, $output) = @_;
    my $content = $c->content // '';
    $$output = "Hello Mojo!$content";
  }
);
$c->stash->{template} = 'something';
$c->stash->{handler}  = 'debug';
is_deeply [$renderer->render($c)], ['Hello Mojo!', 'test'], 'normal rendering';

# Normal rendering with custom format
$c->stash->{format}   = 'something';
$c->stash->{template} = 'something';
$c->stash->{handler}  = 'debug';
is_deeply [$renderer->render($c)], ['Hello Mojo!', 'something'],
  'normal rendering';

# Normal rendering with layout
delete $c->stash->{format};
$c->stash->{template} = 'something';
$c->stash->{layout}   = 'something';
$c->stash->{handler}  = 'debug';
is_deeply [$renderer->render($c)], ['Hello Mojo!Hello Mojo!', 'test'],
  'normal rendering with layout';

# Rendering a path with dots
$c->stash->{template} = 'some.path.with.dots/template';
$c->stash->{handler}  = 'debug';
is_deeply [$renderer->render($c)], ['Hello Mojo!', 'test'],
  'rendering a path with dots';

# Unrecognized handler
my $log = '';
my $cb  = $c->app->log->on(message => sub { $log .= pop });
$c->stash->{handler} = 'not_defined';
is $renderer->render($c), undef, 'return undef for unrecognized handler';
like $log, qr/No handler for "not_defined" found/, 'right message';
$c->app->log->unsubscribe(message => $cb);

# Default template name
$c->stash(controller => 'foo', action => 'bar');
is $c->app->renderer->template_for($c), 'foo/bar', 'right template name';

# Big cookie
$log = '';
$cb  = $c->app->log->on(message => sub { $log .= pop });
$c->cookie(foo => 'x' x 4097);
like $log, qr/Cookie "foo" is bigger than 4096 bytes/, 'right message';
$c->app->log->unsubscribe(message => $cb);

# Nested helpers
my $first = $app->build_controller;
$first->helpers->app->log->level('fatal');
$first->app->helper('myapp.multi_level.test' => sub {'works!'});
ok $first->app->renderer->get_helper('myapp'),                  'found helper';
ok $first->app->renderer->get_helper('myapp.multi_level'),      'found helper';
ok $first->app->renderer->get_helper('myapp.multi_level.test'), 'found helper';
is $first->myapp->multi_level->test, 'works!', 'right result';
is $first->helpers->myapp->multi_level->test, 'works!', 'right result';
$first->app->helper('myapp.defaults' => sub { shift->app->defaults(@_) });
ok $first->app->renderer->get_helper('myapp.defaults'), 'found helper';
is $first->app->renderer->get_helper('myap.'),          undef, 'no helper';
is $first->app->renderer->get_helper('yapp'),           undef, 'no helper';
$first->myapp->defaults(foo => 'bar');
is $first->myapp->defaults('foo'), 'bar', 'right result';
is $first->helpers->myapp->defaults('foo'), 'bar', 'right result';
is $first->app->myapp->defaults('foo'),     'bar', 'right result';
my $app2   = Mojolicious->new(secrets => ['works']);
my $second = $app2->build_controller;
$second->app->log->level('fatal');
is $second->app->renderer->get_helper('myapp'),          undef, 'no helper';
is $second->app->renderer->get_helper('myapp.defaults'), undef, 'no helper';
$second->app->helper('myapp.defaults' => sub {'nothing'});
my $myapp = $first->myapp;
is $first->myapp->defaults('foo'),  'bar',     'right result';
is $second->myapp->defaults('foo'), 'nothing', 'right result';
is $second->helpers->myapp->defaults('foo'), 'nothing', 'right result';
is $first->myapp->defaults('foo'), 'bar', 'right result';
is $first->helpers->myapp->defaults('foo'), 'bar', 'right result';

# Reuse proxy objects
my $helpers = $first->helpers;
is $helpers->myapp->multi_level->test, $helpers->myapp->multi_level->test,
  'same result';

# Compression (disabled)
my $output = 'a' x 1000;
$c = $app->build_controller;
$c->req->headers->accept_encoding('gzip');
$renderer->respond($c, $output, 'html');
is $c->res->headers->content_type, 'text/html;charset=UTF-8',
  'right "Content-Type" value';
ok !$c->res->headers->vary,             'no "Vary" value';
ok !$c->res->headers->content_encoding, 'no "Content-Encoding" value';
is $c->res->body, $output, 'same string';

# Compression (enabled)
$renderer->compress(1);
$c = $app->build_controller;
$c->req->headers->accept_encoding('gzip');
$renderer->respond($c, $output, 'html');
is $c->res->headers->content_type, 'text/html;charset=UTF-8',
  'right "Content-Type" value';
is $c->res->headers->vary, 'Accept-Encoding', 'right "Vary" value';
is $c->res->headers->content_encoding, 'gzip', 'right "Content-Encoding" value';
isnt $c->res->body, $output, 'different string';
is gunzip($c->res->body), $output, 'same string';

# Compression (not requested)
$c = $app->build_controller;
$renderer->respond($c, $output, 'html');
is $c->res->code, 200, 'right status';
is $c->res->headers->content_type, 'text/html;charset=UTF-8',
  'right "Content-Type" value';
is $c->res->headers->vary, 'Accept-Encoding', 'right "Vary" value';
ok !$c->res->headers->content_encoding, 'no "Content-Encoding" value';
is $c->res->body, $output, 'same string';

# Compression (other transfer encoding)
$c = $app->build_controller;
$c->res->headers->content_encoding('whatever');
$renderer->respond($c, $output, 'html', 500);
is $c->res->code, 500, 'right status';
is $c->res->headers->content_type, 'text/html;charset=UTF-8',
  'right "Content-Type" value';
is $c->res->headers->vary, 'Accept-Encoding', 'right "Vary" value';
is $c->res->headers->content_encoding, 'whatever',
  'right "Content-Encoding" value';
is $c->res->body, $output, 'same string';

# Compression (below minimum length)
$output = 'a' x 850;
$c      = $app->build_controller;
$c->req->headers->accept_encoding('gzip');
$renderer->respond($c, $output, 'html');
is $c->res->headers->content_type, 'text/html;charset=UTF-8',
  'right "Content-Type" value';
ok !$c->res->headers->vary,             'no "Vary" value';
ok !$c->res->headers->content_encoding, 'no "Content-Encoding" value';
is $c->res->body, $output, 'same string';

# Missing method (AUTOLOAD)
my $class = ref $first->myapp;
eval { $first->myapp->missing };
like $@, qr/^Can't locate object method "missing" via package "$class"/,
  'right error';
eval { $first->app->myapp->missing };
like $@, qr/^Can't locate object method "missing" via package "$class"/,
  'right error';

# No leaky namespaces
my $helper_class = ref $second->myapp;
is ref $second->myapp, $helper_class, 'same class';
ok $helper_class->can('defaults'), 'helpers are active';
my $template_class = decode 'UTF-8',
  $second->render_to_string(inline => "<%= __PACKAGE__ =%>");
is decode('UTF-8', $second->render_to_string(inline => "<%= __PACKAGE__ =%>")),
  $template_class, 'same class';
ok $template_class->can('stash'), 'helpers are active';
undef $app2;
ok !$helper_class->can('defaults'), 'helpers have been cleaned up';
ok !$template_class->can('stash'),  'helpers have been cleaned up';

done_testing();
