# Copyright (c) 2025 Yuki Kimoto
# MIT License

class Mojolicious::Command::Morbo extends Mojolicious::Command {
  version_from Mojolicious;
  
  use Mojo::Server::Morbo;
  use Mojo::Util;
  use Fn;
  use Sys;
  
  # Class Methods
  static method new : Mojolicious::Command::Morbo () {
    
    my $self = new Mojolicious::Command::Morbo;
    
    $self->init;
    
    return $self;
  }
  
  # Instance Methods
  protected method init : void () {
    
    $self->SUPER::init;
    
    $self->{description} = "Morbo HTTP and WebSocket development server";
    
    $self->{usage} = <<'EOS';
Usage: morbo [OPTIONS] [APPLICATION]

  ./myapp.spvm morbo
  ./myapp.spvm morbo -m production -l https://*:443 -l http://[::]:3000
  ./myapp.spvm morbo -l 'https://*:443?cert=./server.crt&key=./server.key'
  ./myapp.spvm morbo -w /usr/local/lib -w public -w myapp.conf

Options:
  -b, --backend <name>           Morbo backend to use for reloading, defaults
                                 to "Poll"
  -h, --help                     Show this message
  -l, --listen <location>        One or more locations you want to listen on,
                                 defaults to the value of MOJO_LISTEN or
                                 "http://*:3000"
  -m, --mode <name>              Operating mode for your application,
                                 defaults to the value of
                                 MOJO_MODE/PLACK_ENV or "development"
  -v, --verbose                  Print details about what files changed to
                                 STDOUT
  -w, --watch <directory/file>   One or more directories and files to watch
                                 for changes, defaults to the application
                                 script as well as the "lib" and "templates"
                                 directories in the current working
                                 directory

EOS
  }
  
  method build_server : Mojo::Server::Morbo ($args : string[] = undef) {
    
    my $values_h = Hash->new({
      help => 0,
      listen => new string[0],
      proxy => new string[0],
      verbose => 0,
    });
    
    my $specs = [
      "b|backend=s",
      "h|help",
      "l|listen=s",
      "m|mode=s",
      "v|verbose",
      "w|watch=s",
    ];
    
    eval { Mojo::Util->getopt(my $_ = [$args], $values_h, $specs); }
    
    if ($@) {
      die $self->usage;
    }
    
    if ($values_h->{"help"}->(int)) {
      print $self->usage;
      Sys->exit(0);
    }
    
    if ($values_h->exists("backend")) {
      my $backend = $values_h->{"backend"}->(string);
      Sys->set_env("SPVM_MOJO_MORBO_BACKEND", $backend);
    }
    
    if ($values_h->exists("mode")) {
      my $mode = $values_h->{"mode"}->(string);
      Sys->set_env("SPVM_MOJO_MODE", $mode);
    }
    
    my $server = Mojo::Server::Morbo->new;
    
    $server->daemon->set_app($self->app);
    
    my $verbose = $values_h->{"verbose"}->(int);
    
    $server->set_silent(!$verbose);
    
    my $listen = (string[])$values_h->get("listen");
    if (@$listen) {
      $server->daemon->set_listen($listen);
    }
    
    my $watch = (string[])$values_h->get("watch");
    if (@$watch) {
      $server->backend->set_watch($watch);
    }
    
    return $server;
  }
  
  method run : void ($args : string[]) {
    
    $self->build_server($args)->run;
  }
  
}