use strict;
use warnings;
use Test2::V0;
use IO::Async::Loop;
use Future::AsyncAwait;
use FindBin;
use lib "$FindBin::Bin/../../lib";

# Cross-repo smoke test: PAGI-Tools' PAGI::SSE->close(reason) must drive the real
# PAGI::Server to end the SSE stream on the wire. Each repo is unit-tested in
# isolation; this proves the whole chain (Tools close() -> sse.close event ->
# server chunked terminator on the wire). The connection itself stays open
# afterward: a clean SSE stream end honors HTTP/1.1 keep-alive (design
# section 11.6), so the client sees the zero-length chunk, not EOF.
#
# Skips unless PAGI-Tools is on @INC, so PAGI-Server's standalone suite stays
# independent. Run it with:
#   prove -I <PAGI-Tools>/lib -lr t/integration/sse-close-end-to-end.t

eval { require Future::IO::Impl::IOAsync; 1 }
    or plan skip_all => 'Future::IO::Impl::IOAsync required for SSE tests';
# The version floor matters: the pre-split PAGI dist (0.001x) also shipped a
# PAGI::SSE, whose close() is synchronous -- awaiting it dies. close() became
# awaitable (async sub) in PAGI-Tools 0.002001.
plan(skip_all => q{Cross-distribution integration test; set INTEGRATION_TEST=1 to run})
    unless $ENV{INTEGRATION_TEST};
eval { require PAGI::Tools; PAGI::Tools->VERSION(0.002001); require PAGI::SSE; 1 }
    or plan skip_all => 'PAGI-Tools 0.002001+ (PAGI::SSE) not installed; run with -I <PAGI-Tools>/lib';

plan skip_all => "Server integration tests not supported on Windows" if $^O eq 'MSWin32';

use PAGI::Server;   # PAGI::SSE is loaded by the require-guard above (skip-safe)
use IO::Socket::INET;

my $loop = IO::Async::Loop->new;

sub create_server {
    my ($app) = @_;
    my $server = PAGI::Server->new(
        app => $app, host => '127.0.0.1', port => 0, quiet => 1, shutdown_timeout => 1,
    );
    $loop->add($server);
    $server->listen->get;
    return $server;
}

sub sse_get {
    my ($port) = @_;
    my $sock = IO::Socket::INET->new(
        PeerAddr => '127.0.0.1', PeerPort => $port, Proto => 'tcp', Timeout => 5,
    ) or return ('', 0);
    print $sock "GET / HTTP/1.1\r\nHost: 127.0.0.1:$port\r\nAccept: text/event-stream\r\n\r\n";
    $sock->blocking(0);
    my $wire = '';
    my $eof  = 0;
    my $deadline = time + 5;
    while (time < $deadline) {
        my $buf;
        my $n = sysread($sock, $buf, 4096);
        if (defined $n && $n > 0) { $wire .= $buf }
        elsif (defined $n && $n == 0) { $eof = 1; last }
        $loop->loop_once(0.05);
    }
    close $sock;
    $loop->loop_once(0.05) for 1 .. 20;
    return ($wire, $eof);
}

subtest 'PAGI::SSE->close(reason) ends the stream over the real server' => sub {
    my $app = async sub {
        my ($scope, $receive, $send) = @_;
        my $sse = PAGI::SSE->new($scope, $receive, $send);

        await $sse->start;
        await $sse->send_event(event => 'tick', data => '1');

        # Tools-level explicit close: sends sse.close, which the server acts on.
        await $sse->close(reason => 'smoke_done');
    };

    my $server = create_server($app);
    my ($wire, $eof) = sse_get($server->port);

    like($wire, qr/HTTP\/1\.1 200/,      '200 OK');
    like($wire, qr/event: tick\n/,        'event delivered');
    like($wire, qr/data: 1\n/,            'data delivered');
    like($wire, qr/\r\n0\r\n\r\n\z/,
        'stream ended with the chunked terminator after PAGI::SSE->close');
    ok(!$eof, 'connection stays open after the clean stream end (keep-alive honored)');

    $server->shutdown->get;
};

done_testing;
