package Mojolicious::Plugin::SentrySDK;
use Mojo::Base 'Mojolicious::Plugin', -signatures;

use Mojolicious;
use Sentry::SDK;
use Try::Tiny;

sub register ($self, $app, $conf) {
  $app->hook(
    before_server_start => sub ($server, $app) {
      Sentry::SDK->init($conf);
    }
  );

  $app->hook(
    around_action => sub ($next, $c, $action, $last) {
      return $next->() unless $last;

      my $req = $c->req;

      Sentry::Hub->get_current_hub()->with_scope(sub ($scope) {
        my %cookies = map { ($_->name, $_->value) } ($req->cookies // [])->@*;
        my $transaction_name = $c->match->endpoint->to_string || '/';
        $scope->set_transaction_name($transaction_name);
        my $transaction = Sentry::SDK->start_transaction(
          {
            name    => $transaction_name,
            op      => 'http.server',
            request => {
              url          => $req->url->to_abs->to_string,
              cookies      => \%cookies,
              method       => $req->method,
              query_string => $req->url->query->to_hash,
              headers      => $req->headers->to_hash,
              env          => \%ENV,
            },
          },
        );
        $scope->set_span($transaction);

        $scope->add_event_processor(
          sub ($event, $hint) {
            my $modules = $event->{modules} //= {};
            $modules->{Mojolicious} = $Mojolicious::VERSION;
            return $event;
          }
        );

        try {
          $next->();
        } catch {
          Sentry::SDK->capture_exception($_);
          $c->reply->exception($_)
        } finally {
          my $status = $c->res->code;
          $transaction->set_http_status($status) if $status;
          $transaction->finish();
        };
      });
    }
  );
}

1;

=encoding utf8

=head1 NAME

Mojolicious::Plugin::SentrySDK - Sentry plugin for Mojolicious

=head1 SYNOPSIS

=head1 DESCRIPTION

=head1 OPTIONS

=head2 register

  my $config = $plugin->register(Mojolicious->new);
  my $config = $plugin->register(Mojolicious->new, \%options);

Register Sentry in L<Mojolicious> application.

=head1 SEE ALSO

L<Sentry::SDK>.

=cut