#!/usr/bin/env perl
## no critic (ControlStructures::ProhibitPostfixControls)
## no critic (InputOutput::RequireCheckedSyscalls)
use strict;
use warnings;

our $VERSION = 0.001;

# ABSTRACT:

# PODNAME: logviewer

# VERSION: generated by DZP::OurPkgVersion

use English qw( -no_match_vars ); # Avoids regex performance penalty in perl 5.18 and earlier
use Getopt::Long qw( :config auto_version auto_help );
use Carp;
use Pod::Usage;
use utf8;
use Term::ANSIColor qw(:constants);

use Const::Fast;
use DateTime;
use DateTime::Format::ISO8601;
use JSON;
use Term::ReadKey;
use Try::Tiny;
use IO::Interactive ();

# Log::Any::Adapter is configured to STDOUT.
# Ensure no Wide character errors.
# binmode STDOUT, q{:encoding(UTF-8)};
use open ':std', IO => ':encoding(UTF-8)';
local $OUTPUT_AUTOFLUSH = 1;

const my $FIELD_LEN_LVL => 5;
const my $DEFAULT_OPTION_LEVEL => uc 'debug';
const my $DEFAULT_OPTION_CATEGORY => q{.*};

my $man = 0;
my $level = $DEFAULT_OPTION_LEVEL;
my $category = $DEFAULT_OPTION_CATEGORY;
GetOptions(
    'man' => \$man,
    'level|l=s' => \&handle_level,
    'category|c=s' => \$category,
) or pod2usage(2);
pod2usage(-exitval => 0, -verbose => 2) if $man;
sub handle_level {
    my ($opt_name, $opt_value) = @_;
    $level = uc $opt_value;
    return;
}

const my %LOG_LEVEL => (
    EMERGENCY => { as_number => 0, color => RED, },
    ALERT     => { as_number => 1, color => RED, },
    CRITICAL  => { as_number => 2, color => RED, },
    ERROR     => { as_number => 3, color => RED, },
    WARNING   => { as_number => 4, color => YELLOW, },
    NOTICE    => { as_number => 5, color => YELLOW, },
    INFO      => { as_number => 6, color => BLACK ON_WHITE, },
    DEBUG     => { as_number => 7, color => GREEN, },
    TRACE     => { as_number => 8, color => WHITE ON_BLACK, },
);

sub main {
    my (@args) = @_;

    my $fh = *STDOUT;
    $fh->autoflush;
    my $json = JSON->new()->utf8(1);
    while( <> ) {
        chomp;
        my $row = $_;
        utf8::encode($row);
        my $log_entry;
        try {
            # $log_entry = $json->decode( $_ );
            $log_entry = JSON->new()->utf8(1)->decode( $_ );
        };
        if( ! defined $log_entry ) {
            print {$fh} "${PROGRAM_NAME}: Cannot decode JSON: $row\n";
            next;
        }
        my $size = 0;
        my $lvl = uc $log_entry->{'lvl'};
        if( $LOG_LEVEL{ $lvl }->{'as_number'} > $LOG_LEVEL{ $level }->{'as_number'} ) {
            next;
        }
        my $epoch = $log_entry->{'epoch'};
        my $dt = DateTime->from_epoch( epoch => $epoch );
        my $ts = DateTime::Format::ISO8601->format_datetime( $dt );
        my $cat = $log_entry->{'cat'};
        next if( $cat !~ m/$category/msx );
        my $msg = $log_entry->{'msg'};
        my $file = $log_entry->{'file'} // '[NOT AVAIL]';
        my $line = $log_entry->{'line'} // '[NOT AVAIL]';
        my $lvl_c = $LOG_LEVEL{ uc $lvl }->{'color'};
        $log_entry = undef;
        print {$fh} $lvl_c;
        print {$fh} '[' . (sprintf "%-${FIELD_LEN_LVL}s", (substr uc $lvl, 0, $FIELD_LEN_LVL) ) . ']';
        print {$fh} RESET;
        $size += 1 + $FIELD_LEN_LVL + 1 + 1;
        print {$fh} q{ };
        print {$fh} $ts;
        $size += (length $ts) + 1;
        print {$fh} q{ };
        print {$fh} $cat;
        $size += (length $cat) + 1;
        print {$fh} q{ };
        print {$fh} BLUE . $msg . RESET;
        print {$fh} q{ };
        $size += (length $msg) + 1;
        if( IO::Interactive::is_interactive() ) {
            my ($term_width, $term_height, $term_width_px, $term_height_px)
                = GetTerminalSize($fh);
            my $field_len = $term_width - $size;
            if( ($size + length (q{in file } . $file . q{ at } . $line)) > $term_width ) {
                # FIXME Not implemented multi row message.
                print {$fh} q{in file } . $file . q{ at } . $line;
            } else {
                print {$fh} sprintf "%${field_len}s", q{in file } . $file . q{ at } . $line;
            }
        } else {
            print {$fh} q{in file } . $file . q{ at } . $line;
        }
        print {$fh} "\n";
    }
    return 0;
}

exit main(@ARGV);

__END__

=pod
=encoding utf8

=head1 SYNOPSIS

logviewer [options]

Options:
    --help
    --man
    --version
    --level
    --category


=head1 OPTIONS

=over

=item B<--help>

Print a brief help message and exits.

=item B<--man>

Prints the manual page and exits.

=item B<--version>

Prints the version and exits.

=item B<--level>

Show messages up to this level.
Default: DEBUG

=item B<--category>

Show messages in this category. Can be regex.
Default: .*

=back


=head1 DESCRIPTION

B<logviewer> shows JSON log files.





=head1 LICENSE AND COPYRIGHT

Copyright (C) 2023-2026 Mikko Koivunalho
Artistic License 2.0
https://www.perlfoundation.org/artistic-license-20.html

=head1 AUTHOR

Mikko Koivunalho <mikko.koivunalho@iki.fi>

=cut
