package SLUB::LZA::Rosetta::TA::Command::log;
use SLUB::LZA::Rosetta::TA -command;
use strict;
use warnings;
use feature qw(say);
use Regexp::Optimizer;
use DateTime;
use DateTime::Format::DateParse;
use namespace::autoclean;
use SLUB::LZA::Rosetta::TA::Log;



# VERSION

# ABSTRACT: module to grep Rosetta's log for ta-tool

sub abstract {"grep server log of Rosetta based Archival Information System";}

my $description=<<"DESCR";
Searches logfiles of Rosetta-based AIS

Examples:

  * What are the error messages in last 24 hours?
    $0 log --level error --last-24h
  * What are error and warning messages between 2022-01-01 and 2022-02-01?
    $0 log --level error --level warning --fromdate 2022-01-01 --todate 2021-02-01
  * Are there lines with regex "ma[tc]?h"?
    $0 log --match "ma[tc]?h"
  * Give me a trace of specific sip
    $0 log --trace sip293144
DESCR

sub description {
    "$description"
}

sub opt_spec {
    return(
        ["verbose|v" => "enable verbose output"],
        ["outputfilter" => hidden => {one_of => [
            ["colorize|c" => "colorize output"],
            ["csv" => "use csv output"],
        ]}],
        ["last-24h" => "search within last 24h"],
        ["fromdate=s" => "search starting with date"],
        ["todate=s" => "search ending with date"],
        ["level=s@" => "levels to search for. Levels could be: 'error', 'warn', 'info', 'debug'. You could use multiple levels by repeating"],
        ["match=s" => "perl regex to search for" => {default=>".*"}],
        ["trace=s" => "trace a sip/IE with given ID (SIP-ID or Deposit-ID or IE PID)"],
        ["user|u:s" => "user if authentication needed (optional, depends on settings in Rosetta's global parameter: sru_authentication=true)", {required=>0}],
        ["password|p:s" => "password if authentication needed (optional, depends on settings in Rosetta's global parameter: sru_authentication=true)", {required=>0}],
    );
}

sub validate_date {
    my $self = shift;
    my $datestr = shift;
    return ($datestr =~ m/^20[0123][0-9]-[0-1][0-9]-[0-3][0-9]$/);
}

sub validate_args {
    my ($self, $opt, $args) = @_;
    # no args allowed but options!
    $self->usage_error("No args allowed") if @$args;
    if (defined $opt->fromdate
        and defined $opt->last_24h
        and $opt->last_24h == 1
    ) {
        $self->usage_error("--last-24h and --fromdate not combinable");
    }
    if (defined $opt->todate
        and defined $opt->last_24h
        and $opt->last_24h == 1
    ) {
        $self->usage_error("--last-24h and --todate not combinable");
    }
    # check dates
    if (defined $opt->fromdate && !$self->validate_date($opt->fromdate)) {
        $self->usage_error("--fromdate $opt->{fromdate} not a valid date");
    }
    if (defined $opt->todate && !$self->validate_date($opt->todate)) {
        $self->usage_error("--todate $opt->{todate} not a valid date");
    }
    # TODO: check levels
    1;
}

sub create_regex_last24h {
    my $dt = DateTime->now;
    my $todate = $dt->ymd;
    my $fromdate= $dt->subtract( hours => 24)->ymd;
    my $rxo = Regexp::Optimizer->new->optimize(qr/$fromdate|$todate/);
    return $rxo;
}

sub create_regex_from_to {
    my $from=shift // "2000-01-01";
    my $to=shift // "2059-12-31";
    if ($from eq "2000-01-01" and $to eq "2059-12-31") {
        return qr{20[0-5][0-9]-[01][0-9]-[0-3][0-9]};
    }
    my $dt_from = DateTime::Format::DateParse->parse_datetime("$from");
    my $dt_to = DateTime::Format::DateParse->parse_datetime("$to");
    my @date_tmo_s;
    for (my $dt = $dt_from; $dt->epoch() <= $dt_to->epoch; $dt->add(days => 1) ) {
        push @date_tmo_s, $dt->ymd;
    }
    my $date_rx_string = join("|", @date_tmo_s);
    my $rxo = Regexp::Optimizer->new->optimize(qr/$date_rx_string/);
    return $rxo;
}

sub execute {
    my ($self, $opt, $args) = @_;
    # create date_rx if provided by CLI
    my $date_rx=qr/[^ ]*/;
    if (defined $opt->last_24h and $opt->last_24h == 1) {
        $date_rx=create_regex_last24h();
    } else {
        $date_rx=create_regex_from_to($opt->fromdate, $opt->todate);
    }
    # create level_rx if multiple levels provided by CLI
    my $level_rx=qr/(DEBUG|INFO|WARN|ERROR)/;
    if (defined $opt->level) {
        my $rx_string = join("|", map {uc} @{ $opt->level });
        $level_rx = Regexp::Optimizer->new->optimize(qr/$rx_string/);
    }
    my $match_rx=qr{$opt->{match}};
    # prepare output filter
    my $output_filter=sub { $_[0]; };
    if (defined $opt->colorize) {
        $output_filter = sub { SLUB::LZA::Rosetta::TA::Log::colorize($_[0], $opt, $match_rx); };
    } elsif (defined $opt->csv) {
        $output_filter = sub { SLUB::LZA::Rosetta::TA::Log::csv($_[0], $opt, $match_rx); };
    }
    # prepare trace
    my $with_trace;
    if (defined $opt->trace) {
        $with_trace = $opt->trace;
        if (defined $opt->colorize) {
            $output_filter = sub { SLUB::LZA::Rosetta::TA::Log::colorize($_[0], $opt, $match_rx); };
        } else {
            $output_filter=sub { $_[0]; };
        }
        SLUB::LZA::Rosetta::TA::Log::trace_log($with_trace, $opt->colorize, $date_rx, $level_rx, $match_rx, $output_filter, );
    } else {
        SLUB::LZA::Rosetta::TA::Log::scan_log($date_rx, $level_rx, $match_rx, $output_filter);
    }
}

1;
