#!/usr/bin/env perl
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/../lib";

use Trickster;
use Trickster::Exception;
use Trickster::Validator;
use Trickster::Logger;
use Trickster::Cookie;
use Plack::Builder;

# Initialize app with logger
my $logger = Trickster::Logger->new(level => 'info');
my $app = Trickster->new(
    logger => $logger,
    debug => 1,
);

# Cookie handler
my $cookie = Trickster::Cookie->new(
    secret => 'your-secret-key-change-in-production',
);

# In-memory data store
my %users = (
    1 => { id => 1, name => 'Alice', email => 'alice@example.com', role => 'admin' },
    2 => { id => 2, name => 'Bob', email => 'bob@example.com', role => 'user' },
);
my $next_id = 3;

# Home route
$app->get('/', sub {
    my ($req, $res) = @_;
    return $res->html(<<'HTML');
<!DOCTYPE html>
<html>
<head><title>Trickster Advanced Example</title></head>
<body>
    <h1>Trickster Advanced Example</h1>
    <h2>API Endpoints:</h2>
    <ul>
        <li>GET /api/users - List all users</li>
        <li>GET /api/users/:id - Get user by ID</li>
        <li>POST /api/users - Create user (requires name, email)</li>
        <li>PUT /api/users/:id - Update user</li>
        <li>DELETE /api/users/:id - Delete user</li>
        <li>GET /api/profile/:username - Get profile (named route)</li>
    </ul>
    <h2>Features Demonstrated:</h2>
    <ul>
        <li>Robust routing with constraints</li>
        <li>Exception handling</li>
        <li>Data validation</li>
        <li>Structured logging</li>
        <li>Secure cookies</li>
        <li>Named routes</li>
    </ul>
</body>
</html>
HTML
});

# List users
$app->get('/api/users', sub {
    my ($req, $res) = @_;
    $logger->info('Listing all users');
    return $res->json([values %users]);
});

# Get user by ID (with numeric constraint)
$app->get('/api/users/:id', sub {
    my ($req, $res) = @_;
    my $id = $req->param('id');
    
    $logger->info("Fetching user", id => $id);
    
    unless (exists $users{$id}) {
        Trickster::Exception::NotFound->throw(
            message => "User with ID $id not found"
        );
    }
    
    return $res->json($users{$id});
}, constraints => { id => qr/^\d+$/ });

# Create user with validation
$app->post('/api/users', sub {
    my ($req, $res) = @_;
    my $data = $req->json;
    
    my $validator = Trickster::Validator->new({
        name => ['required', ['min', 2], ['max', 50]],
        email => ['required', 'email'],
        role => [['in', 'admin', 'user', 'guest']],
    });
    
    unless ($validator->validate($data)) {
        $logger->warn('Validation failed', errors => $validator->errors);
        Trickster::Exception::BadRequest->throw(
            message => 'Validation failed',
            details => $validator->errors,
        );
    }
    
    my $user = {
        id => $next_id++,
        name => $data->{name},
        email => $data->{email},
        role => $data->{role} || 'user',
    };
    
    $users{$user->{id}} = $user;
    
    $logger->info('User created', id => $user->{id}, name => $user->{name});
    
    return $res->json($user, 201);
});

# Update user
$app->put('/api/users/:id', sub {
    my ($req, $res) = @_;
    my $id = $req->param('id');
    my $data = $req->json;
    
    unless (exists $users{$id}) {
        Trickster::Exception::NotFound->throw(
            message => "User with ID $id not found"
        );
    }
    
    my $validator = Trickster::Validator->new({
        name => [['min', 2], ['max', 50]],
        email => ['email'],
        role => [['in', 'admin', 'user', 'guest']],
    });
    
    unless ($validator->validate($data)) {
        Trickster::Exception::BadRequest->throw(
            message => 'Validation failed',
            details => $validator->errors,
        );
    }
    
    $users{$id}{name} = $data->{name} if $data->{name};
    $users{$id}{email} = $data->{email} if $data->{email};
    $users{$id}{role} = $data->{role} if $data->{role};
    
    $logger->info('User updated', id => $id);
    
    return $res->json($users{$id});
}, constraints => { id => qr/^\d+$/ });

# Delete user
$app->delete('/api/users/:id', sub {
    my ($req, $res) = @_;
    my $id = $req->param('id');
    
    unless (exists $users{$id}) {
        Trickster::Exception::NotFound->throw(
            message => "User with ID $id not found"
        );
    }
    
    delete $users{$id};
    
    $logger->info('User deleted', id => $id);
    
    return $res->json({ success => 1, message => 'User deleted' });
}, constraints => { id => qr/^\d+$/ });

# Named route example
$app->get('/api/profile/:username', sub {
    my ($req, $res) = @_;
    my $username = $req->param('username');
    
    # Find user by name
    my ($user) = grep { lc($_->{name}) eq lc($username) } values %users;
    
    unless ($user) {
        Trickster::Exception::NotFound->throw(
            message => "Profile for $username not found"
        );
    }
    
    return $res->json($user);
}, name => 'user_profile');

# Cookie example
$app->get('/cookie/set', sub {
    my ($req, $res) = @_;
    $cookie->set($res, 'user_session', 'session-123', max_age => 3600);
    return $res->json({ message => 'Cookie set' });
});

$app->get('/cookie/get', sub {
    my ($req, $res) = @_;
    my $session = $cookie->get($req, 'user_session');
    return $res->json({ session => $session || 'none' });
});

# Custom error handler
$app->error_handler(sub {
    my ($error, $req, $res) = @_;
    
    if (ref($error) && $error->isa('Trickster::Exception')) {
        $logger->warn('Exception thrown', 
            status => $error->status,
            message => $error->message
        );
        return $res->json($error->as_hash, $error->status)->finalize;
    }
    
    $logger->error('Unhandled error', error => "$error");
    return $res->json({ error => 'Internal Server Error' }, 500)->finalize;
});

# Wrap with middleware
builder {
    enable 'Runtime';
    enable 'ContentLength';
    $app->to_app;
};