#!/usr/bin/env perl
#
use Object::Pad ':experimental(:all)';

package perldoc_browser;

class perldoc_browser;

use v5.40;
use v5.40;

use Plack::Builder;
use Plack::Middleware::Rewrite;
use Plack::Middleware::ReverseProxy;
use Plack::Middleware::StackTrace;
use Plack::Middleware::Debug;
use Path::Try;
use List::Util qw'uniqstr any';
use Getopt::Long;
use IO::Handle::Common;
use Data::Printer;
use PadWalker qw'var_name peek_my';

field $builder : reader = Plack::Builder->new;
field $pathmap : reader : param = {};
field $execdir : reader = [];

method refstr : common ($ref) {
    return reftype($ref) // '';
}

method adjust ( $field, $val ) {
    my $varname = var_name( 0, $field );
    eval "$varname = \$val" if $varname;
    $val;
}

method mount_middlware (@arg) {
    $builder->add_middleware_if(
        sub ($env) {
            !$$env{REMOTE_ADDR}
              || any { $$env{REMOTE_ADDR} eq $_ } qw(127.0.0.1 ::1);
        },
        "Plack::Middleware::ReverseProxy"
    );

    if ( $ENV{PLACK_ENV} && ( $ENV{PLACK_ENV} eq 'development' ) ) {
        $builder->add_middleware('Debug');
        $builder->add_middleware('StackTrace');
    }
}

method mount_pathmap {
    foreach my ( $root, $mount ) ( $pathmap->@{qw'root mount'} ) {
        dmsg $self, $root, $mount;
        $builder->mount( $mount => Plack::Util::load_psgi($root) );
    }
}

method to_psgi {
    $self->mount_pathmap;
    dmsg $self;

    $self->mount_middlware;
    dmsg $self;

    $builder->to_app;
}

method to_app { $self->to_psgi(@_) }

# *to_app = \&to_psgi;

package perldoc_browser::CLI;

use utf8;
use v5.40;

use List::Util qw'any';
use Getopt::Long
  qw(GetOptionsFromArray :config no_ignore_case auto_abbrev passthrough bundling long_prefix_pattern=--?);
use IO::Handle::Common;
use Const::Fast;

sub cli ( $argv = \@ARGV ) {
    our %cliopt;

    my $pathmap_multival_err = sub {
        state $count //= 0;
        my $errstr = "Local root and URI mount path have already been set: ";

        $errstr .=
"Use only the 'root:mount' format as a value for --pathmap () or as positional arguments to set multiple custom mappings.";

        $count++;
    };

    const my %baremap => ( 0 => 'root', 1 => 'mount' );

    GetOptionsFromArray(
        $argv,
        \%cliopt,
        'pathmap:s',
        'srvroot:s',
        'mount:s',
        '<>' => sub ($bare) {
            state $count //= 0;

            if ( my ( $root, $mount ) = ( $bare =~ /^([^:]+):(.+)$/ ) ) {
                if ( $count || any { $_ } @cliopt{qw'root mount'} ) {
                    pathmap_multival_err();
                }

                $cliopt{pathmap} = { root => $root, mount => $mount };
            }
            else {
                if ( $count > 1 ) {
                    pathmap_multival_err();
                }
                $cliopt{ $baremap{$count} } = $bare;
            }

        }
    );

    shift @$argv
      if ( perldoc_browser->refstr($argv) eq 'ARRAY'
        && @$argv[0] eq '--' );

    return %cliopt;
}

my $app  = perldoc_browser->new( cli( \@ARGV ) );
my $psgi = $app->to_psgi;

unless (caller) {
    require Plack::Runner;

    my $runner = Plack::Runner->new;
    $runner->parse_options(@ARGV);

    $runner->run($psgi);

    error "$! ($?)" if $? > 0;
    exit $?;
}

return $psgi

