use strict;
use warnings;
use experimental 'signatures';
use Scalar::Util 'weaken';

use Mojo::IOLoop;
use Mojo::UserAgent;

my ($port, $websocket_address) = @ARGV;
die "Syntax: $0 port wss://address/\n"
    unless defined $port && defined $websocket_address
    && $port =~ /^\d+$/ && $websocket_address =~ m|://|;

my $ua = Mojo::UserAgent->new;
$ua->inactivity_timeout(0);
my %connt;

Mojo::IOLoop->server(
    { port => $port }
    => sub ($loop, $stream, $id) {
	$stream->timeout(0);
	$stream->on(
	    read
	    => sub ($stream, $bytes) {
		if (my $tx = $connt{$id}{wstx}) {
		    my @msgs = split /\r?\n/, $bytes;
		    $tx->send($_) for @msgs;
		}
		else {
		    print "buffering...\n";
		    $connt{$id}{buf} .= $bytes;
		}
	    }
	   );
	$stream->on(
	    close
	    => sub ($stream) {
		if (my $tx = $connt{$id}{wstx}) {
		    $tx->finish;
		}
	    }
	   );
	print "opening ws\n";
	$ua->websocket(
	    $websocket_address
	    => { 'Sec-WebSocket-Extensions' => 'permessage-deflate' }
	    => sub ($ua, $tx) {
		unless ($tx->is_websocket) {
		    print "handshake failed\n";
		    $stream->write("ERROR :websocket handshake failed\r\n");
		    $stream->close_gracefully;
		    return;
		}

		$connt{$id}{wstx} = $tx;
		weaken($connt{$id}{wstx});

		print "websocket ready\n";
		$tx->on(
		    finish
		    => sub ($tx, $code, $reason) {
			print "disconnected ($code)\n";
			delete $connt{$id}{wstx};
			$stream->write("ERROR :websocket closed with status $code\r\n");
			$stream->close_gracefully;
		    }
		   );
		$tx->on(
		    message
		    => sub ($tx, $msg) {
			print "irc connected\n" if $msg =~ /^:\S+ 001 /;
			$stream->write("$msg\r\n");
		    }
		   );

		my $buf = delete $connt{$id}{buf};
		return unless defined $buf;
		my @msgs = split /\r?\n/, $buf;
		print "sending buffer...\n";
		$tx->send($_) for @msgs;
	    }
	   );
    });
print "listening\n";

Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
