package Test::Foo;
use Mojo::Base 'Mojolicious::Controller';

sub bar  {1}
sub home {1}

package Test::Controller;
use Mojo::Base 'Mojolicious::Controller';

has 'render_called';

sub render { shift->render_called(1) }

sub reset_state {
  my $self = shift;
  $self->render_called(0);
  my $stash = $self->stash;
  delete $stash->{$_} for keys %$stash;
}

# "I was all of history's greatest acting robots -- Acting Unit 0.8,
#  Thespomat, David Duchovny!"
package main;
use Mojo::Base -strict;

use utf8;

use Test::More tests => 85;

use Mojo::Upload;
use Mojolicious;
use Mojo::Transaction::HTTP;
use Mojolicious::Controller;
use Mojolicious::Routes;

# Fresh controller
my $c = Mojolicious::Controller->new;
is $c->url_for('/'), '/', 'routes are working';

# Set
$c->stash(foo => 'bar');
is $c->stash('foo'), 'bar', 'set and return a stash value';

# Ref value
my $stash = $c->stash;
is_deeply $stash, {foo => 'bar'}, 'return a hashref';

# Replace
$c->stash(foo => 'baz');
is $c->stash('foo'), 'baz', 'replace and return a stash value';

# Set 0
$c->stash(zero => 0);
is $c->stash('zero'), 0, 'set and return 0 value';

# Replace with 0
$c->stash(foo => 0);
is $c->stash('foo'), 0, 'replace and return 0 value';

# Use 0 as key
$c->stash(0 => 'boo');
is $c->stash('0'), 'boo', 'set and get with 0 as key';

# Delete
$stash = $c->stash;
delete $stash->{foo};
delete $stash->{0};
delete $stash->{zero};
is_deeply $stash, {}, 'elements can be deleted';
$c->stash('foo' => 'zoo');
delete $c->stash->{foo};
is_deeply $c->stash, {}, 'elements can be deleted';

# Set via hash
$c->stash({a => 1, b => 2});
$stash = $c->stash;
is_deeply $stash, {a => 1, b => 2}, 'set via hashref';

# Override captures
is $c->param('foo'), undef, 'no value';
is $c->param(foo => 'works')->param('foo'), 'works', 'right value';
is $c->param(foo => 'too')->param('foo'),   'too',   'right value';
is $c->param(foo => qw/just works/)->param('foo'), 'just', 'right value';
is_deeply [$c->param('foo')], [qw/just works/], 'right values';
is $c->param(foo => undef)->param('foo'), undef, 'no value';
is $c->param(foo => Mojo::Upload->new(name => 'bar'))->param('foo')->name,
  'bar', 'right value';

# Controller with application and routes
$c = Test::Controller->new(app => Mojolicious->new);
$c->app->log->path(undef);
$c->app->log->level('fatal');
my $d = Mojolicious::Routes->new;
ok $d, 'initialized';
$d->namespace('Test');
$d->route('/')->over([])->to(controller => 'foo', action => 'home');
$d->route('/foo/(capture)')->to(controller => 'foo', action => 'bar');

# Cache
$c->reset_state;
my $tx = Mojo::Transaction::HTTP->new;
$tx->req->method('GET');
$tx->req->url->parse('/');
$c->tx($tx);
ok $d->dispatch($c), 'dispatched';
is $c->stash->{controller}, 'foo',  'right value';
is $c->stash->{action},     'home', 'right value';
ok $c->render_called, 'rendered';
my $cache = $d->cache->get('GET:/:0');
ok $cache, 'route has been cached';
$c->reset_state;
$tx = Mojo::Transaction::HTTP->new;
$tx->req->method('GET');
$tx->req->url->parse('/');
$c->tx($tx);
ok $d->dispatch($c), 'dispatched';
is $c->stash->{controller}, 'foo',  'right value';
is $c->stash->{action},     'home', 'right value';
ok $c->render_called, 'rendered';
is_deeply $d->cache->get('GET:/:0'), $cache, 'cached route has been reused';

# 404 clean stash
$c->reset_state;
$tx = Mojo::Transaction::HTTP->new;
$tx->req->method('GET');
$tx->req->url->parse('/not_found');
$c->tx($tx);
ok !$d->dispatch($c), 'not dispatched';
is_deeply $c->stash, {}, 'empty stash';
ok !$c->render_called, 'nothing rendered';

# No escaping
$c->reset_state;
$tx = Mojo::Transaction::HTTP->new;
$tx->req->method('POST');
$tx->req->url->parse('/foo/hello');
$c->tx($tx);
$c->stash(test => 23);
ok $d->dispatch($c), 'dispatched';
is $c->stash->{controller}, 'foo',   'right value';
is $c->stash->{action},     'bar',   'right value';
is $c->stash->{capture},    'hello', 'right value';
is $c->stash->{test},       23,      'right value';
isa_ok $c->stash->{'mojo.captures'}, 'HASH', 'right captures';
is $c->param('controller'), undef,   'no value';
is $c->param('action'),     undef,   'no value';
is $c->param('capture'),    'hello', 'right value';
is_deeply [$c->param], ['capture'], 'right names';
$c->param(capture => 'bye');
is $c->param('controller'), undef, 'no value';
is $c->param('action'),     undef, 'no value';
is $c->param('capture'),    'bye', 'right value';
is_deeply [$c->param], ['capture'], 'right names';
$c->param(capture => undef);
is $c->param('controller'), undef, 'no value';
is $c->param('action'),     undef, 'no value';
is $c->param('capture'),    undef, 'no value';
is_deeply [$c->param], ['capture'], 'no names';
$c->req->param(foo => 'bar');
is $c->param('controller'), undef, 'no value';
is $c->param('action'),     undef, 'no value';
is $c->param('capture'),    undef, 'no value';
is $c->param('foo'),        'bar', 'right value';
is_deeply [$c->param], [qw/capture foo/], 'right names';
$c->req->param(bar => 'baz');
is $c->param('controller'), undef, 'no value';
is $c->param('action'),     undef, 'no value';
is $c->param('capture'),    undef, 'no value';
is $c->param('foo'),        'bar', 'right value';
is $c->param('bar'),        'baz', 'right value';
is_deeply [$c->param], [qw/bar capture foo/], 'right names';
$c->req->param(action => 'baz');
is $c->param('controller'), undef, 'no value';
is $c->param('action'),     'baz', 'no value';
is $c->param('capture'),    undef, 'no value';
is $c->param('foo'),        'bar', 'right value';
is $c->param('bar'),        'baz', 'right value';
is_deeply [$c->param], [qw/action bar capture foo/], 'right names';
ok $c->render_called, 'rendered';

# Escaping
$c->reset_state;
$tx = Mojo::Transaction::HTTP->new;
$tx->req->method('GET');
$tx->req->url->parse('/foo/hello%20there');
$c->tx($tx);
ok $d->dispatch($c), 'dispatched';
is $c->stash->{controller}, 'foo',         'right value';
is $c->stash->{action},     'bar',         'right value';
is $c->stash->{capture},    'hello there', 'right value';
isa_ok $c->stash->{'mojo.captures'}, 'HASH', 'right captures';
is $c->param('controller'), undef,         'no value';
is $c->param('action'),     undef,         'no value';
is $c->param('capture'),    'hello there', 'right value';
ok $c->render_called, 'rendered';

# Escaping utf8
$c->reset_state;
$tx = Mojo::Transaction::HTTP->new;
$tx->req->method('GET');
$tx->req->url->parse('/foo/%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82');
$c->tx($tx);
ok $d->dispatch($c), 'dispatched';
is $c->stash->{controller}, 'foo',          'right value';
is $c->stash->{action},     'bar',          'right value';
is $c->stash->{capture},    'привет', 'right value';
isa_ok $c->stash->{'mojo.captures'}, 'HASH', 'right captures';
is $c->param('controller'), undef,          'no value';
is $c->param('action'),     undef,          'no value';
is $c->param('capture'),    'привет', 'right value';
ok $c->render_called, 'rendered';
