#!/usr/bin/env raku

use Datastar::SDK;
use Datastar::Example::HTTP;

constant MESSAGE = 'Hello, world!';
constant DEFAULT-PORT = 1337;
constant PAGE = $?FILE.IO.parent.add('hello-world.html');

sub delay-milliseconds(%signals) {
    my $delay = %signals<delay> // 0;
    die 'delay must be a non-negative number'
        unless $delay ~~ Numeric && $delay >= 0;
    $delay;
}

sub stream-message(%request, $connection --> Nil) {
    my $request = SocketRequest.new(
        :method(%request<method>),
        target => %request<target>,
        body => %request<body>,
        protocol => %request<protocol>,
    );

    my %signals;
    try %signals = read-signals($request);
    if $! {
        $connection.print(http-response(400, $!.message));
        return;
    }

    my $delay;
    try $delay = delay-milliseconds(%signals);
    if $! {
        $connection.print(http-response(400, $!.message));
        return;
    }
    my $response = SocketResponse.new(connection => $connection);
    my $sse = ServerSentEventGenerator.new(
        request => $request,
        response => $response,
    );

    for ^MESSAGE.chars -> $index {
        $sse.patch-elements(
            '<div id="message">'
            ~ MESSAGE.substr(0, $index + 1)
            ~ '</div>'
        );
        sleep($delay / 1000) if $delay > 0;
    }
}

sub handle-request(%request, $connection, Str:D $page --> Nil) {
    if %request<method> ne 'GET' {
        $connection.print(http-response(405, 'method not allowed'));
    }
    elsif %request<path> eq '/' {
        $connection.print(http-response(
            200,
            $page,
            content-type => 'text/html; charset=utf-8',
        ));
    }
    elsif %request<path> eq '/hello-world' {
        stream-message(%request, $connection);
    }
    else {
        $connection.print(http-response(404, 'not found'));
    }
}

sub MAIN(Int $port = DEFAULT-PORT) {
    die "missing example page: {PAGE.Str}" unless PAGE.f;
    my $page = PAGE.slurp;

    serve(
        -> %request, $connection {
            handle-request(%request, $connection, $page);
        },
        :$port,
    );
}
