package Medusa::XS;

use 5.014;
use strict;
use warnings;

# Required modules for XS Data::Dumper calls
use Data::Dumper ();

our $VERSION = '0.01';

require XSLoader;
XSLoader::load('Medusa::XS', $VERSION);

# Configuration hash - kept in Perl for user customizability
# All LOGIC is in XS, this is just configuration data
our %LOG;
our %AUDITED;

BEGIN {
    %LOG = (
        LOGGER      => 'Medusa::XS::Logger',
        LOG_LEVEL   => 'debug',
        LOG_FILE    => 'audit.log',
        LOG_INIT    => sub {
            (my $module = $LOG{LOGGER}) =~ s/::/\//g;
            require $module . '.pm';
            $LOG{LOGGER}->new(
                file => $LOG{LOG_FILE},
            );
        },
        TIME        => 'gmtime',
        TIME_FORMAT => 'default',
        LOG         => undef,
        QUOTE       => '†',
        OPTIONS     => {
            date         => 1,
            guid         => 1,
            level        => 1,
            elapsed_call => 1,
            caller       => 1,
        },
        LOG_FUNCTIONS => {
            error => 'error',
            info  => 'info',
            debug => 'debug',
        },
        # Default FORMAT_MESSAGE - wrapper around XS xs_format_message
        # Users can override with their own sub
        FORMAT_MESSAGE => \&xs_format_message
    );
}

# import(), MODIFY_CODE_ATTRIBUTES(), and FETCH_CODE_ATTRIBUTES()
# are implemented in XS for maximum performance

1;

__END__

=encoding utf8

=head1 NAME

Medusa::XS - High-performance XS audit logging with the C<:Audit> attribute

=head1 VERSION

Version 0.01

=head1 SYNOPSIS

    package MyApp::User;
    use Medusa::XS;

    sub create :Audit {
        my ($self, %args) = @_;
        # ... your code ...
        return $user;
    }

    # Every call to create() is now automatically logged:
    #   entry  - arguments, caller stack, GUID, timestamp
    #   exit   - return values, elapsed time

=head1 DESCRIPTION

Medusa::XS is a drop-in XS replacement for L<Medusa> that provides automatic
subroutine audit logging via Perl attributes.  Marking a subroutine with the
C<:Audit> attribute causes every call to be logged with entry arguments, return
values, a unique GUID, caller stack trace, timestamp and elapsed time.

All hot-path work is done in C: GUID generation, caller-stack walking,
timestamp formatting, argument serialisation, and log dispatch.  The only
Perl-level data is the C<< %Medusa::XS::LOG >> configuration hash.

=head1 USAGE

=head2 Applying the C<:Audit> attribute

    use Medusa::XS;

    sub my_method :Audit {
        ...
    }

When the attribute is compiled, B<Medusa::XS> wraps the subroutine with an
XS wrapper that logs entry and exit automatically.

=head2 Passing configuration at import

    use Medusa::XS (
        LOG_LEVEL   => 'info',
        LOG_FILE    => '/var/log/myapp-audit.log',
        TIME        => 'localtime',
        TIME_FORMAT => '%Y-%m-%dT%H:%M:%S.%ms',
    );

Key-value pairs are merged into C<< %Medusa::XS::LOG >>.

=head1 CONFIGURATION

All configuration lives in the package variable C<< %Medusa::XS::LOG >>.
Defaults are set at C<BEGIN> time and may be overridden before or after
C<use Medusa::XS>.

=over 4

=item B<LOGGER> I<(string, default C<"Medusa::XS::Logger">)>

Class name of the logger to instantiate when C<LOG_INIT> is called.

=item B<LOG_FILE> I<(string, default C<"audit.log">)>

Path passed to the logger constructor.

=item B<LOG_LEVEL> I<(string, default C<"debug">)>

The log level written into each message.  Also selects which method is
called on the logger object (via C<LOG_FUNCTIONS>).

=item B<LOG_INIT> I<(coderef)>

Called once (lazily, on the first audited call) to create the logger object.
Must return a blessed reference that is stored in C<< $LOG{LOG} >>.

=item B<LOG> I<(object, default C<undef>)>

The live logger instance.  Set automatically by C<LOG_INIT>, or assign your
own object before any audited call:

    $Medusa::XS::LOG{LOG} = My::Logger->new;

The object must implement the method named by C<LOG_FUNCTIONS> for the
current C<LOG_LEVEL> (e.g. C<debug>, C<info>, or C<error>).

=item B<TIME> I<(string, default C<"gmtime">)>

C<"gmtime"> or C<"localtime"> — controls the time zone of timestamps.

=item B<TIME_FORMAT> I<(string, default C<"default">)>

A C<strftime(3)>-compatible format string.  The special token C<%ms> is
replaced with zero-padded milliseconds.  The literal string C<"default">
produces C<strftime("%a %b %e %H:%M:%S %Y")>.

=item B<QUOTE> I<(string, default C<"†"> (U+2020 DAGGER))>

Delimiter wrapped around field values in the formatted log line.

=item B<FORMAT_MESSAGE> I<(coderef, default C<\&xs_format_message>)>

Formatter callback.  When set to the built-in C<xs_format_message> (or left
at the default), the fast all-C formatting path is used.  Supply your own
coderef to customize log output:

    $Medusa::XS::LOG{FORMAT_MESSAGE} = sub {
        my (%p) = @_;
        # %p contains: message, guid, caller, level, params, prefix,
        #              elapsed_call (on exit)
        return "CUSTOM: $p{message}";
    };

B<Note:> a custom Perl callback disables the zero-allocation fast path.

=item B<LOG_FUNCTIONS> I<(hashref)>

Maps log level names to method names on the logger object:

    {
        error => 'error',
        info  => 'info',
        debug => 'debug',
    }

=item B<OPTIONS> I<(hashref)>

Feature flags controlling which fields appear in each log line.  All
default to B<1> (enabled):

    {
        date         => 1,   # timestamp
        guid         => 1,   # unique call ID
        level        => 1,   # log level label
        caller       => 1,   # caller stack trace
        elapsed_call => 1,   # elapsed time (exit messages only)
    }

=back

=head1 FUNCTIONS

These are available as C<Medusa::XS::function_name()> and are implemented
entirely in C.

=head2 generate_guid

    my $uuid = Medusa::XS::generate_guid();

Returns a UUID v4 string (36 characters).  Uses C</dev/urandom> where
available, falling back to C<rand()>.

=head2 format_time

    my $ts = Medusa::XS::format_time();             # gmtime, default fmt
    my $ts = Medusa::XS::format_time(0);            # localtime
    my $ts = Medusa::XS::format_time(1, '%Y-%m-%d');

Returns a formatted timestamp string.  First argument selects gmtime (true)
or localtime (false); second is an optional C<strftime> format.

=head2 collect_caller_stack

    my $stack = Medusa::XS::collect_caller_stack();

Walks Perl's context stack in C and returns a string like
C<"main:12-E<gt>Foo::Bar:34-E<gt>Baz::Qux:56">.

=head2 clean_dumper

    my $cleaned = Medusa::XS::clean_dumper($raw_dumper_output);

Single-pass cleanup of L<Data::Dumper> output: strips C<$VAR1 = >,
trailing semicolons, and excess whitespace.

=head2 dump_sv

    my $str = Medusa::XS::dump_sv($value);

Pure-C serialiser that produces compact Perl-like output for any SV
(scalars, array/hash refs, blessed objects, coderefs, regexps).  Handles
circular references.  Used internally for argument logging.

=head2 is_audited

    if (Medusa::XS::is_audited(\&Some::Sub)) { ... }

Returns true if the code reference has been wrapped with C<:Audit>.

=head2 wrap_sub

    Medusa::XS::wrap_sub(\&Some::Sub);
    Medusa::XS::wrap_sub(\&Some::Sub, 'method_name');

Programmatically wraps a subroutine with audit logging (same effect as
the C<:Audit> attribute).  Optional second argument overrides the method
name used in log messages.

=head2 log_message

    Medusa::XS::log_message(
        message => 'something happened',
        guid    => $guid,
        caller  => $stack,
        params  => \@args,
        prefix  => 'arg',
    );

Low-level: formats and dispatches a single log entry through the
configured C<FORMAT_MESSAGE> and logger.

=head2 xs_format_message

    my $line = Medusa::XS::xs_format_message(%params);

The default C<FORMAT_MESSAGE> implementation, written in C.  Accepts the
same keys as C<log_message> and returns a formatted string.

=head2 init_logger

    Medusa::XS::init_logger();

Calls C<< $LOG{LOG_INIT} >> if C<< $LOG{LOG} >> is not yet set.
Normally called automatically on the first audited subroutine call.

=head1 SEE ALSO

L<Medusa::XS::Logger> — the default XS file logger.

=head1 LICENSE

This module is free software; you may redistribute and/or modify it under
the same terms as Perl itself.

=cut
