use Mojo::Base -strict;

use Test::More;
use Mojo::Headers;

# Basic functionality
my $headers = Mojo::Headers->new;
$headers->add(Connection => 'close');
$headers->add(Connection => 'keep-alive');
is_deeply $headers->every_header('Connection'), ['close', 'keep-alive'],
  'right structure';
is $headers->header('Connection'), 'close, keep-alive', 'right value';
$headers->remove('Connection');
is $headers->header('Connection'), undef, 'no value';
is $headers->connection, undef, 'no value';
$headers->content_type('text/text')->content_type('text/html');
$headers->expect('continue-100');
$headers->connection('close');
is $headers->content_type, 'text/html',                'right value';
like $headers->to_string,  qr/.*\x0d\x0a.*\x0d\x0a.*/, 'right format';
my $hash = $headers->to_hash;
is $hash->{Connection},     'close',        'right value';
is $hash->{Expect},         'continue-100', 'right value';
is $hash->{'Content-Type'}, 'text/html',    'right value';
$hash = $headers->to_hash(1);
is_deeply $hash->{Connection},     ['close'],        'right structure';
is_deeply $hash->{Expect},         ['continue-100'], 'right structure';
is_deeply $hash->{'Content-Type'}, ['text/html'],    'right structure';
is_deeply [sort @{$headers->names}], [qw(Connection Content-Type Expect)],
  'right structure';
$headers->expires('Thu, 01 Dec 1994 16:00:00 GMT');
$headers->cache_control('public');
is $headers->expires,       'Thu, 01 Dec 1994 16:00:00 GMT', 'right value';
is $headers->cache_control, 'public',                        'right value';
$headers->etag('abc321');
is $headers->etag, 'abc321', 'right value';
is $headers->header('ETag'), $headers->etag, 'values are equal';
$headers->status('200 OK');
is $headers->status, '200 OK', 'right value';
is $headers->header('Status'), $headers->status, 'values are equal';

# Mixed case
is $headers->header('content-type' => 'text/plain')->content_type,
  'text/plain', 'right value';
is $headers->header('Content-Type' => 'text/html')->content_type, 'text/html',
  'right value';

# Common headers
$headers = Mojo::Headers->new;
is $headers->accept('foo')->accept,                   'foo', 'right value';
is $headers->accept_charset('foo')->accept_charset,   'foo', 'right value';
is $headers->accept_encoding('foo')->accept_encoding, 'foo', 'right value';
is $headers->accept_language('foo')->accept_language, 'foo', 'right value';
is $headers->accept_ranges('foo')->accept_ranges,     'foo', 'right value';
is $headers->access_control_allow_origin('foo')->access_control_allow_origin,
  'foo', 'right value';
is $headers->allow('foo')->allow,                 'foo', 'right value';
is $headers->authorization('foo')->authorization, 'foo', 'right value';
is $headers->connection('foo')->connection,       'foo', 'right value';
is $headers->cache_control('foo')->cache_control, 'foo', 'right value';
is $headers->content_disposition('foo')->content_disposition, 'foo',
  'right value';
is $headers->content_encoding('foo')->content_encoding, 'foo', 'right value';
is $headers->content_language('foo')->content_language, 'foo', 'right value';
is $headers->content_length('foo')->content_length,     'foo', 'right value';
is $headers->content_location('foo')->content_location, 'foo', 'right value';
is $headers->content_range('foo')->content_range,       'foo', 'right value';
is $headers->content_security_policy('foo')->content_security_policy, 'foo',
  'right value';
is $headers->content_type('foo')->content_type,           'foo', 'right value';
is $headers->cookie('foo')->cookie,                       'foo', 'right value';
is $headers->dnt('foo')->dnt,                             'foo', 'right value';
is $headers->date('foo')->date,                           'foo', 'right value';
is $headers->etag('foo')->etag,                           'foo', 'right value';
is $headers->expect('foo')->expect,                       'foo', 'right value';
is $headers->expires('foo')->expires,                     'foo', 'right value';
is $headers->host('foo')->host,                           'foo', 'right value';
is $headers->if_modified_since('foo')->if_modified_since, 'foo', 'right value';
is $headers->last_modified('foo')->last_modified,         'foo', 'right value';
is $headers->link('foo')->link,                           'foo', 'right value';
is $headers->location('foo')->location,                   'foo', 'right value';
is $headers->origin('foo')->origin,                       'foo', 'right value';
is $headers->proxy_authenticate('foo')->proxy_authenticate, 'foo',
  'right value';
is $headers->proxy_authorization('foo')->proxy_authorization, 'foo',
  'right value';
is $headers->range('foo')->range, 'foo', 'right value';
is $headers->sec_websocket_accept('foo')->sec_websocket_accept, 'foo',
  'right value';
is $headers->sec_websocket_extensions('foo')->sec_websocket_extensions, 'foo',
  'right value';
is $headers->sec_websocket_key('foo')->sec_websocket_key, 'foo', 'right value';
is $headers->sec_websocket_protocol('foo')->sec_websocket_protocol, 'foo',
  'right value';
is $headers->sec_websocket_version('foo')->sec_websocket_version, 'foo',
  'right value';
is $headers->server('foo')->server,               'foo', 'right value';
is $headers->server_timing('foo')->server_timing, 'foo', 'right value';
is $headers->set_cookie('foo')->set_cookie,       'foo', 'right value';
is $headers->status('foo')->status,               'foo', 'right value';
is $headers->strict_transport_security('foo')->strict_transport_security,
  'foo', 'right value';
is $headers->te('foo')->te,                               'foo', 'right value';
is $headers->trailer('foo')->trailer,                     'foo', 'right value';
is $headers->transfer_encoding('foo')->transfer_encoding, 'foo', 'right value';
is $headers->upgrade('foo')->upgrade,                     'foo', 'right value';
is $headers->user_agent('foo')->user_agent,               'foo', 'right value';
is $headers->vary('foo')->vary,                           'foo', 'right value';
is $headers->www_authenticate('foo')->www_authenticate,   'foo', 'right value';

# Clone
$headers = Mojo::Headers->new;
$headers->add(Connection => 'close');
$headers->add(Connection => 'keep-alive');
is $headers->header('Connection'), 'close, keep-alive', 'right value';
my $clone = $headers->clone;
$headers->connection('nothing');
is $headers->header('Connection'), 'nothing',           'right value';
is $clone->header('Connection'),   'close, keep-alive', 'right value';
$headers = Mojo::Headers->new;
$headers->expect('100-continue');
is $headers->expect, '100-continue', 'right value';
$clone = $headers->clone;
$clone->expect('nothing');
is $headers->expect, '100-continue', 'right value';
is $clone->expect,   'nothing',      'right value';
$clone = Mojo::Headers->new->add(Foo => [qw(bar baz)])->clone;
is_deeply $clone->to_hash(1)->{Foo}, [[qw(bar baz)]], 'right structure';
$clone = $headers->clone;
$headers->add(Expect => 'whatever');
is_deeply $clone->to_hash(1), {Expect => ['100-continue']}, 'right structure';
is_deeply $headers->to_hash(1), {Expect => ['100-continue', 'whatever']},
  'right structure';

# Parse headers
$headers = Mojo::Headers->new;
isa_ok $headers->parse(<<'EOF'), 'Mojo::Headers', 'right return value';
Content-Type: text/plain
o: x
Expect: 100-continue
Cache-control: public
Expires: Thu, 01 Dec 1994 16:00:00 GMT

EOF
ok $headers->is_finished,   'parser is finished';
is $headers->content_type,  'text/plain', 'right value';
is $headers->expect,        '100-continue', 'right value';
is $headers->cache_control, 'public', 'right value';
is $headers->expires,       'Thu, 01 Dec 1994 16:00:00 GMT', 'right value';
is $headers->header('o'), 'x', 'right value';

# Parse multi-line headers
$headers = Mojo::Headers->new;
$headers->parse(<<'EOF');
Foo: first
 second
 third
Content-Type: text/plain
Foo Bar: baz
Foo: first again
  second ":again"

EOF
ok $headers->is_finished, 'parser is finished';
$hash = {
  'Content-Type' => ['text/plain'],
  Foo            => ['first second third', 'first again second ":again"'],
  'Foo Bar'      => ['baz']
};
is_deeply $headers->to_hash(1), $hash, 'right structure';
is $headers->header('Foo'), 'first second third, first again second ":again"',
  'right value';
$headers = Mojo::Headers->new->parse($headers->to_string . "\x0d\x0a\x0d\x0a");
ok $headers->is_finished, 'parser is finished';
is_deeply $headers->to_hash(1), $hash, 'successful roundtrip';
$hash = {
  'Content-Type' => 'text/plain',
  Foo            => 'first second third, first again second ":again"',
  'Foo Bar'      => 'baz'
};
is_deeply $headers->to_hash, $hash, 'right structure';

# Set headers from hash
$headers = Mojo::Headers->new;
$headers->from_hash({Connection => 'close', 'Content-Type' => 'text/html'});
is_deeply $headers->to_hash,
  {Connection => 'close', 'Content-Type' => 'text/html'}, 'right structure';

# Remove all headers
$headers->from_hash({});
is_deeply $headers->to_hash, {}, 'right structure';

# Append values
$headers = Mojo::Headers->new;
$headers->vary('Accept');
$headers->append(Vary => 'Accept-Encoding');
is $headers->vary, 'Accept, Accept-Encoding', 'right value';
$headers = Mojo::Headers->new;
$headers->append(Vary => 'Accept');
is $headers->vary, 'Accept', 'right value';
$headers->append(Vary => 'Accept-Encoding');
is $headers->vary, 'Accept, Accept-Encoding', 'right value';
$headers = Mojo::Headers->new;
$headers->add(Vary => 'Accept', 'Accept-Encoding');
is_deeply $headers->to_hash(1), {Vary => ['Accept', 'Accept-Encoding']},
  'right structure';
$headers->append(Vary => 'Accept-Language');
is_deeply $headers->to_hash(1),
  {Vary => ['Accept, Accept-Encoding, Accept-Language']}, 'right structure';

# Multiple headers with the same name
$headers = Mojo::Headers->new;
$headers->from_hash(
  {'X-Test' => [23, 24], 'X-Test2' => 'foo', Connection => ['a', 'b']});
$hash = $headers->to_hash;
is $hash->{'X-Test'},   '23, 24', 'right value';
is $hash->{'X-Test2'},  'foo',    'right value';
is $hash->{Connection}, 'a, b',   'right value';
$hash = $headers->to_hash(1);
is_deeply $hash->{'X-Test'}, [23, 24], 'right structure';
is_deeply $hash->{'X-Test2'}, ['foo'], 'right structure';
is_deeply $hash->{Connection}, ['a', 'b'], 'right structure';
$headers = Mojo::Headers->new->parse($headers->to_string . "\x0d\x0a\x0d\x0a");
is_deeply $headers->to_hash(1),
  {'X-Test' => [23, 24], 'X-Test2' => ['foo'], Connection => ['a', 'b']},
  'right structure';
is $headers->header('X-Test'), '23, 24', 'right value';
is $headers->connection, 'a, b', 'right value';
is $headers->connection('c', 'd', 'e')->connection, 'c, d, e', 'right value';

# Headers in chunks
$headers = Mojo::Headers->new;
isa_ok $headers->parse(<<EOF), 'Mojo::Headers', 'right return value';
Content-Type: text/plain
EOF
ok !$headers->is_finished, 'parser is not finished';
ok !defined($headers->content_type), 'no value';
isa_ok $headers->parse(<<EOF), 'Mojo::Headers', 'right return value';
X-Bender: Bite my shiny
EOF
ok !$headers->is_finished, 'parser is not finished';
ok !defined($headers->connection), 'no value';
isa_ok $headers->parse(<<EOF), 'Mojo::Headers', 'right return value';
X-Bender: metal ass!

EOF
ok $headers->is_finished, 'parser is finished';
is $headers->content_type, 'text/plain', 'right value';
is $headers->header('X-Bender'), 'Bite my shiny, metal ass!', 'right value';

# Dehop
$headers = Mojo::Headers->new;
my $fail = {
  Connection            => 'fail',
  'Keep-Alive'          => 'fail',
  'Proxy-Authenticate'  => 'fail',
  'Proxy-Authorization' => 'fail',
  Server                => 'pass',
  TE                    => 'fail',
  Trailer               => 'fail',
  'Transfer-Encoding'   => 'fail',
  Upgrade               => 'fail'
};
$headers->from_hash($fail);
is_deeply $headers->to_hash, $fail, 'right structure';
is_deeply $headers->dehop->to_hash, {Server => 'pass'}, 'right structure';

# Invalid characters
$headers = Mojo::Headers->new;
eval { $headers->add(Foo => "test\x0a") };
like $@, qr/Invalid characters in Foo header/, 'right error';
eval { $headers->header(Foo => "test\x0d") };
like $@, qr/Invalid characters in Foo header/, 'right error';
eval { $headers->append(Foo => "\x0atest") };
like $@, qr/Invalid characters in Foo header/, 'right error';
eval { $headers->header(Bar => "\x0a\x0dtest") };
like $@, qr/Invalid characters in Bar header/, 'right error';

done_testing();
