package MCP::Server::Transport::Stdio;
use Mojo::Base 'MCP::Server::Transport', -signatures;

use MCP::Server::Context;
use Mojo::JSON qw(decode_json encode_json);
use Mojo::Log;
use Scalar::Util qw(blessed);

sub handle_requests ($self) {
  my $server = $self->server;

  binmode STDIN,  ':raw';
  binmode STDOUT, ':raw';
  STDOUT->autoflush(1);

  my $buffer = '';
  while (defined(my $input = _read_line(\$buffer))) {
    next if $input eq '';
    my $request = eval { decode_json($input) };
    next unless my $response = $server->handle($request, MCP::Server::Context->new(transport => $self));

    if (blessed($response) && $response->isa('Mojo::Promise')) {
      $response->then(sub { _print_response($_[0]) })->wait;
    }
    else { _print_response($response) }
  }
}

sub notify ($self, $session_id, $method, $params = {}) {
  _print_response({jsonrpc => '2.0', method => $method, params => $params});
  return 1;
}

sub notify_all ($self, $method, $params = {}) { $self->notify(undef, $method, $params) }

sub _print_response ($response) { print encode_json($response) . "\n" }

sub _read_line ($buffer) {
  while (index($$buffer, "\n") < 0) {
    last unless sysread STDIN, my $chunk, 131072;
    $$buffer .= $chunk;
  }
  return undef if $$buffer eq '';

  my $pos  = index($$buffer, "\n");
  my $line = $pos < 0 ? substr($$buffer, 0, length($$buffer), '') : substr($$buffer, 0, $pos + 1, '');
  $line =~ s/\r?\n?$//;
  return $line;
}

1;

=encoding utf8

=head1 NAME

MCP::Server::Transport::Stdio - Stdio transport for MCP servers

=head1 SYNOPSIS

  use MCP::Server::Transport::Stdio;

  my $stdio = MCP::Server::Transport::Stdio->new;

=head1 DESCRIPTION

L<MCP::Server::Transport::Stdio> is a transport for MCP (Model Context Protocol) server that reads requests from
standard input (STDIN) and writes responses to standard output (STDOUT). It is designed for command-line tools and
debugging tasks.

=head1 ATTRIBUTES

L<MCP::Server::Transport::Stdio> inherits all attributes from L<MCP::Server::Transport>.

=head1 METHODS

L<MCP::Server::Transport::Stdio> inherits all methods from L<MCP::Server::Transport> and implements the following new
ones.

=head2 handle_requests

  $stdio->handle_requests;

Reads requests from standard input and prints responses to standard output.

=head2 notify

  my $bool = $stdio->notify($session_id, $method);
  my $bool = $stdio->notify($session_id, $method, {foo => 'bar'});

Send a JSON-RPC notification to standard output. The C<$session_id> is ignored.

=head2 notify_all

  my $bool = $stdio->notify_all($method);
  my $bool = $stdio->notify_all($method, {foo => 'bar'});

Send a JSON-RPC notification to standard output.

=head1 SEE ALSO

L<MCP>, L<https://mojolicious.org>, L<https://modelcontextprotocol.io>.

=cut
