The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package JSTAPd::Server::Contents;
use strict;
use warnings;
use Plack::Response;
use Data::UUID;

sub handler {
    my($class, $path, $server, $req, $session, $args) = @_;

    my @chain  = split '/', $path;
    my $method = pop @chain;
    my $klass  = join '::', __PACKAGE__, @chain;

    if (@chain && $chain[0] eq 'contents') {
        shift @chain;
        $klass  = 'JSTAPd::Server::Contents::contents';
        $method = join '/', @chain, $method;
    } else {
        no strict 'refs';
        eval 'require $klass' unless %{"$klass\::"}; ## no critic
    }
    unless (!$@ && ($klass->can($method) || $klass->can('AUTOLOAD'))) {
        return Plack::Response->new(404, [ 'Content-Type' => 'text/plain' ], 'Not Found' );
    }
    warn "$klass -> $method : " . $req->uri unless $server->run_once;
    $klass->$method($server, $req, $session, $args);
}

# index page
sub index {
    my($class, $server, $req, $session) = @_;
    return Plack::Response->new(
        200, 
        [ 'Content-Type' => 'text/html' ],
        JSTAPd::Contents->build_index(
            jstapd_prefix => $server->jstapd_prefix,
            run_once      => $server->run_once,
            auto_open     => $server->auto_open,
        )
    );
}

package JSTAPd::Server::Contents::contents;
use JSON::XS ();

sub AUTOLOAD {
    my($class, $server, $req, $session, $args) = @_;
    my $path = our $AUTOLOAD;
    $path =~ s/.+:://;
    my @chain = split '/', $path;
    my $basename = pop @chain;

    if ($basename eq 'index') {
        return _index(@_, \@chain);
    }

    # foo.t
    $server->setup_session_tap($session, $path);
    $server->set_tests($session, $path);

    my $content = $server->contents->fetch_file($basename, \@chain);

    if ($args->{is_api}) {
        return __run_api($content, $server, $req, $session, $args);
    }

    my @include = map {
        ref($_) eq 'SCALAR' ? sprintf "include('/%s/share/js/%s')", $server->jstapd_prefix, ${ $_ } : "include('$_')"
    } $content->suite->include_ex, $content->suite->include;

    my $index   = $server->contents->fetch_file('index', \@chain, 1);
    return Plack::Response->new(
        200,
        [ 'Content-Type' => 'text/html' ],
        $index->build_html(
            $content->header(
                jstapd_prefix => $server->jstapd_prefix,
                session       => $session,
                path          => $path,
                include       => join('.', @include),
            ),
            $content->suite->html_body,
        ),
    );
}

sub __run_api {
    my($content, $server, $req, $session, $args) = @_;
    my $tap = $server->get_tap($session, $args->{path});

    my $GLOBAL = $tap->global_stash;
    my $METHOD = $req->method;
    my $PATH   = $req->uri->path;

    my $ret;
    my $err;
    do {
        local $@;
        eval {
            $ret = $content->suite->server_api(
                $tap->global_stash,
                $req,
                $req->method,
                $req->uri->path,
            );
        };
        $err = $@;
    };
    if ($err) {
        my $body = sprintf "error: %s\n\t%s\n", $args->{path}, $err;
        warn $body;
        return Plack::Response->new(500, [ 'Content-Type' => 'text/plain' ], $body );
    }
    if (ref($ret)) {
        return Plack::Response->new(200, [ 'Content-Type' => 'application/json' ], JSON::XS->new->ascii->encode($ret) );
    } else {
        return Plack::Response->new(200, [ 'Content-Type' => 'text/plain' ], $ret );
    }
}

package JSTAPd::Server::Contents::js;

sub AUTOLOAD {
    my($class, $server, $req, $session, $args) = @_;
    my $path = our $AUTOLOAD;
    $path =~ s/.+:://;
    $path =~ s/\.js$//;
    $path =~ s/-/_/;

    my $body;
    $body = $class->$path if $class->can($path);
    return Plack::Response->new( 404, [ 'Content-Type' => 'text/plain' ], 'Not Found' ) unless $body;
    return Plack::Response->new( 200, [ 'Content-Type' => 'text/plain' ], $body );
}

1;