package SLUB::LZA::Rosetta::TA::Log;
use strict;
use warnings;
use feature qw(say);
use SLUB::LZA::Rosetta::TA::SOAP;
use Path::Tiny;
use namespace::autoclean;

# VERSION

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

sub helper_scan_log {
    my $directory = shift;
    my $fh_processing = shift;
    for ($directory->children( qr/^server.log/ )) {
        my $file = $_;
        if (!$file->is_file) { next; }
        my $fh;
        if ($file =~ m/\.gz$/) {
            $fh = IO::Zlib->new("$file", "rb");
        } else {
            $fh = $file->filehandle;
        }
        if (defined $fh) {
            $fh_processing->( $fh, $file );
        }
        undef $fh;
    }
    return 1;
}

sub trace_log {
    my $with_trace=shift;
    my $with_color=shift;
    my $date_rx=shift;
    my $level_rx=shift;
    my $match_rx=shift;
    my $output_filter=shift;
    my $directory = path($SLUB::LZA::Rosetta::TA::config{logdir});
    my $deposit_id;
    my $deposit_dir;
    my $sip_id;
    my $rep_id;
    my $ie_pid;
    my $searchid = $with_trace;
    $searchid=~s/^(REP|SIP|IE)(\d+)$/$2/;
    say "SEARCHID=$searchid";
    # match to:
    # 1. ... | processing {originalDirName=eb9c1924-4bab-11ec-baca-f69de10fbd49, depositId=422950, ... userName=Goobi_SMA, SIP 422438, producerType=TRUSTED, producerGroup=PG_Goobi, contentStructure=METS, materialFlowSR=0, producerId=264981, contentStructureId=264951, converter_class_name=com.exlibris.dps.deposit.converters.METSCSConverter, retentionPoliciesId=NO_RETENTION} from work queue SIP_LOADING_WORK_QUEUE finished
    # 2. ...     enqueued {originalDirName=d2cb9509-4bad-11ec-baca-b925eea982a1, depositId=422958, ... userName=Goobi_SMA, sipId=422446, producerType=TRUSTED, producerGroup=PG_Goobi, contentStructure=METS, materialFlowSR=0, producerId=264981, contentStructureId=264951, converter_class_name=com.exlibris.dps.deposit.converters.METSCSConverter, retentionPoliciesId=NO_RETENTION} on work queue V2SL_shr00.SIP_LOADING_WORK_QUEUE
    # 3. ... SIP 13156, Deposit Activity ID=17589Properties
    my $sip_rx = qr/(SIP |sipId=)/;
    my $datetime_rx = qr/$date_rx \d\d:\d\d:\d\d,\d\d\d/;
    my $pre_rx = qr/$datetime_rx INFO  /;

    my $line_rx1 = Regexp::Optimizer->new->optimize(qr{^$pre_rx.*(originalDirName=|depositId=|$sip_rx)$searchid});
    my $line_rx2 = Regexp::Optimizer->new->optimize(qr{^$pre_rx.*(SIP |Deposit Activity ID=)$searchid});
    my $line_rx3 = Regexp::Optimizer->new->optimize(qr{^$pre_rx.*Loaded \d+ files for: .*$searchid});
    my $line_rx4 = Regexp::Optimizer->new->optimize(qr{^$pre_rx.*((Representation $searchid IE \d+)|(Representation \d+ IE $searchid)) Copy ID: \d+});
    if ($with_trace =~ m/^SIP/) { # search specific sip
        $sip_id=$searchid;
        $ie_pid=get_ie_pid_by_sip($sip_id);
        $line_rx1 = Regexp::Optimizer->new->optimize(qr{^$pre_rx.*($sip_rx)$searchid});
        $line_rx2 = $line_rx1;
    } elsif ($with_trace =~ m/^IE/) { # search specific IE
        $ie_pid=$searchid;
        $line_rx3 = Regexp::Optimizer->new->optimize(qr{^$pre_rx.*Loaded \d+ files for: REP\d+ \(IE$searchid\)});
        $line_rx4 = Regexp::Optimizer->new->optimize(qr{^$pre_rx.*Representation \d+ IE $searchid Copy ID: \d+});
    } elsif ($with_trace =~ m/^REP/) {
        $rep_id=$searchid;
        $line_rx3 = Regexp::Optimizer->new->optimize(qr{^$pre_rx.*Loaded \d+ files for: REP$searchid \(IE\d+\)});
        $line_rx4 = Regexp::Optimizer->new->optimize(qr{^$pre_rx.*Representation $searchid IE \d+ Copy ID: \d+});
    }
    my $fh_processing_stage1 = sub {
        my $fh = shift;
        my $file = shift;
        my $file_md5 = path($file)->digest();
        return if (
            exists( $SLUB::LZA::Rosetta::TA::cache{$file_md5} )
                and (
                (       exists($SLUB::LZA::Rosetta::TA::cache{$file_md5}->{deposit_dir})
                    and exists($SLUB::LZA::Rosetta::TA::cache{$file_md5}->{deposit_id})
                    and exists($SLUB::LZA::Rosetta::TA::cache{$file_md5}->{sip_id})
                    and exists($SLUB::LZA::Rosetta::TA::cache{$file_md5}->{deposit_dir}->{$searchid})
                    and exists($SLUB::LZA::Rosetta::TA::cache{$file_md5}->{deposit_id}->{$searchid})
                    and exists($SLUB::LZA::Rosetta::TA::cache{$file_md5}->{sip_id}->{$searchid})
                ) or (
                    exists($SLUB::LZA::Rosetta::TA::cache{$file_md5}->{rep_id})
                        and exists($SLUB::LZA::Rosetta::TA::cache{$file_md5}->{ie_pid})
                        and exists($SLUB::LZA::Rosetta::TA::cache{$file_md5}->{ie_pid}->{$searchid})
                        and exists($SLUB::LZA::Rosetta::TA::cache{$file_md5}->{rep_id}->{$searchid})
                )
            )
        );
        while(<$fh>) {
            if (
                (defined $sip_id and defined $deposit_id and defined $deposit_dir)
                    or (defined $ie_pid and defined $rep_id)
            ) { last; }
            if (!m/^$pre_rx/) {
                next;
            }
            if (!m/$searchid/) {
                next;
            }
            chomp;
            if ( m/$line_rx1/ ) {
                if (!defined $sip_id      and m/$sip_rx(\d{6}),/) {
                    $sip_id = $2;
                    if (!defined $ie_pid) {
                        $ie_pid=get_ie_pid_by_sip($sip_id);
                    }
                    $SLUB::LZA::Rosetta::TA::cache{$file_md5}->{sip_id}->{$sip_id}=1; # mark as match
                }
                if (!defined $deposit_dir and m/originalDirName=([^,]*),/) {
                    $deposit_dir = $1;
                    $SLUB::LZA::Rosetta::TA::cache{$file_md5}->{deposit_dir}->{$deposit_dir}=1; # mark as match
                }
                if (!defined $deposit_id  and m/depositId=(\d+),/) {
                    $deposit_id = $1;
                    $SLUB::LZA::Rosetta::TA::cache{$file_md5}->{deposit_id}->{$deposit_id}=1; # mark as match
                }

            } elsif (m/$line_rx2/) {
                if (!defined $sip_id and m/SIP (\d{6})/) {
                    $sip_id = $1;
                    if (!defined $ie_pid) {
                        $ie_pid=get_ie_pid_by_sip($sip_id);
                    }
                    $SLUB::LZA::Rosetta::TA::cache{$file_md5}->{sip_id}->{$sip_id}=1; # mark as match
                }
                if (!defined $deposit_id and m/Deposit Activity ID=(\d+)/) {
                    $deposit_id = $1;
                    $SLUB::LZA::Rosetta::TA::cache{$file_md5}->{deposit_id}->{$deposit_id}=1; # mark as match
                }
            } elsif (m/$line_rx3/) {
                if (!defined $ie_pid and m/Loaded \d+ files for: REP\d+ \((IE\d+)/) {
                    $ie_pid = $1;
                    $SLUB::LZA::Rosetta::TA::cache{$file_md5}->{ie_pid}->{$ie_pid}=1; # mark as match
                }
                if (!defined $rep_id and m/Loaded \d+ files for: (REP\d+)/) {
                    $rep_id = $1;
                    $SLUB::LZA::Rosetta::TA::cache{$file_md5}->{rep_id}->{$rep_id}=1; # mark as match
                }
            } elsif (m/$line_rx4/) {
                my $rx = qr/Representation (\d+) IE (\d+)/;
                if (!defined $ie_pid and m/$rx/) {
                    $ie_pid = $2;
                    $SLUB::LZA::Rosetta::TA::cache{$file_md5}->{ie_pid}->{$ie_pid}=1; # mark as match
                }
                if (!defined $rep_id and m/$rx/) {
                    $rep_id = $1;
                    $SLUB::LZA::Rosetta::TA::cache{$file_md5}->{rep_id}->{$rep_id}=1; # mark as match
                }
            }
        }
        return 1;
    };
    helper_scan_log($directory, $fh_processing_stage1);
    no warnings;
    my $match= sprintf("found: DIR=%s, DEPOSITID=%d, SIPID=%s, IEPID=%s, REPID=%s",
        $deposit_dir ? $deposit_dir : "----",
        $deposit_id ?  $deposit_id  : "----",
        $sip_id ?      $sip_id      : "----",
        $ie_pid ?      "IE".$ie_pid : "----",
        $rep_id ?      "REP".$rep_id: "----"
    );
    use warnings;
    say "$match";
    say "-"x(length($match));
    # now call scan_log and use own colorizer
    if ($with_color) {
        $output_filter = sub {colorize_trace($_[0], $deposit_dir, $deposit_id, $sip_id, $ie_pid, $rep_id)};
    }
    my $search_rxo = Regexp::Optimizer->new->optimize(qr/^$date_rx [^ ]* $level_rx  (.*?)$match_rx(.*?)$/);
    no warnings;
    my $nextline_rx1= Regexp::Optimizer->new->optimize(qr{originalDirName=$deposit_dir|(depositID|Deposit Activity ID)[= ]$deposit_id|(sipId|SIP[= ]?)$sip_id});
    my $nextline_rx2=Regexp::Optimizer->new->optimize(qr{IE[ ]?$ie_pid|REP[ ]?$rep_id});
    use warnings;
    my $fh_processing_stage2 = sub {
        my $fh = shift;
        my $file = shift;
        my $file_md5 = path($file)->digest();
        return if ! (
            exists( $SLUB::LZA::Rosetta::TA::cache{$file_md5} )
                and
                (
                    (exists ($SLUB::LZA::Rosetta::TA::cache{$file_md5}->{deposit_dir})  and exists($SLUB::LZA::Rosetta::TA::cache{$file_md5}->{deposit_dir}->{$deposit_dir}))
                        or (exists ($SLUB::LZA::Rosetta::TA::cache{$file_md5}->{deposit_id})   and exists($SLUB::LZA::Rosetta::TA::cache{$file_md5}->{deposit_id}->{$deposit_id}))
                        or (exists ($SLUB::LZA::Rosetta::TA::cache{$file_md5}->{sip_id})       and exists($SLUB::LZA::Rosetta::TA::cache{$file_md5}->{sip_id}->{$sip_id}))
                        or (exists ($SLUB::LZA::Rosetta::TA::cache{$file_md5}->{ie_pid}) and exists($SLUB::LZA::Rosetta::TA::cache{$file_md5}->{ie_pid}->{$ie_pid}))
                        or (exists ($SLUB::LZA::Rosetta::TA::cache{$file_md5}->{rep_id}) and exists($SLUB::LZA::Rosetta::TA::cache{$file_md5}->{rep_id}->{$rep_id}))
                )
        );
        while(<$fh>) {
            if (!m/^$date_rx/) {
                next;
            }
            if (! m/$search_rxo/) {
                #print "no match for '$_'";
                next;
            }
            if (
                (
                    defined $deposit_dir
                        and defined $deposit_id
                        and defined $sip_id
                        and !m/$nextline_rx1/
                ) or (
                    defined $ie_pid
                        and defined $rep_id
                        and !m/$nextline_rx2/
                )
            ) {
                next;
            }
            chomp;
            my $line = $output_filter->( $_ );
            say $line;
        }
    };
    helper_scan_log($directory, $fh_processing_stage2);
}

sub scan_log {
    my $date_rx=shift;
    my $level_rx=shift;
    my $match_rx=shift;
    my $output_filter=shift;
    # open dir from config{$logdir}
    # for all files matching server.log*; do
    #   read lines
    #   filter lines
    #   return
    my $directory = path($SLUB::LZA::Rosetta::TA::config{logdir});
    my $search_rxo = Regexp::Optimizer->new->optimize(qr/^$date_rx [^ ]* $level_rx  (.*?)$match_rx(.*?)$/);
    my $fh_processing = sub {
        my $fh = shift;
        while(<$fh>) {
            if (! m/$search_rxo/) {
                #print "no match for '$_'";
                next;
            }
            chomp;
            my $line = $output_filter->( $_ );
            say $line;
        }
    };
    helper_scan_log($directory, $fh_processing);
}


{
    my $bred = "\e[1;31m";
    my $red = "\e[31m";
    my $green = "\e[32m";
    my $blue = "\e[34m";
    my $bblue = "\e[1;34m";
    my $gray = "\e[90m]";
    my $reversed = "\e[7m";
    my $reset = "\e[0m";
    my $back_yellow = "\e[103m";
    my $back_cyan = "\e[45m";
    my $back_green = "\e[43m";
    my $datetime_rx=qr/\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d,\d\d\d/;
    sub colorize {
        my $line = shift;
        my $opt = shift;
        my $match_rx = shift;
        # patterns in common interest:
        $line =~ s/^($datetime_rx)/${blue}$1${reset}/;
        if ($opt->{match} ne ".*") {
            $line =~ s/( (DEBUG|INFO|WARN|ERROR)  .*?)($match_rx)/$1${reversed}$3${reset}/; # order important!
        }
        $line =~ s/ (DEBUG) / ${gray}$1${reset} /
            || $line =~ s/ (INFO) / ${green}$1${reset} /
            || $line =~ s/ (WARN) / ${red}$1${reset} /
            || $line =~ s/ (ERROR) / ${bred}$1${reset} /;
        $line =~ s/(SIP ?\d+)/${back_yellow}$1${reset}/g;
        $line =~ s/(IE ?\d+)/${back_yellow}$1${reset}/g;
        $line =~ s/(dc.identifier)/${back_cyan}$1${reset}/g;
        return $line;
    }
    sub colorize_trace {
        my $line = shift;
        my $deposit_dir = shift;
        my $deposit_id = shift;
        my $sip_id = shift;
        my $ie_pid = shift;
        my $rep_id = shift;
        # patterns in common interest:
        $line =~ s/^($datetime_rx)/${blue}$1${reset}/;
        $line =~ s/ (DEBUG) / ${gray}$1${reset} /
            || $line =~ s/ (INFO) / ${green}$1${reset} /
            || $line =~ s/ (WARN) / ${red}$1${reset} /
            || $line =~ s/ (ERROR) / ${bred}$1${reset} /;
        if (defined $deposit_dir) {$line =~ s/($deposit_dir)/${back_green}$1${reset}/g;}
        if (defined $deposit_id ) {$line =~ s/(dep_|Deposit Activity ID=|depositId=)($deposit_id)/$1${back_green}$2${reset}/g;}
        if (defined $sip_id     ) {$line =~ s/((sipId|SIP|PID|pid)[ =]?)($sip_id)/$1${back_green}$3${reset}/g;}
        if (defined $ie_pid     ) {$line =~ s/(IE ?)($ie_pid)/$1${back_green}$2${reset}/g;}
        if (defined $rep_id     ) {$line =~ s/(Representation )($rep_id)/$1${back_green}$2${reset}/g;}
        return $line;
    }
}

{
    my $csv;
    sub csv {
        my $line = shift;
        my $opt = shift;
        my $match_rx = shift;
        my $ret;
        if (!defined $csv) {
            $csv = Text::CSV_PP->new(
                {
                    sep_char => ";",

                }
            );
            $ret=join(";", qw(date time level where msg))."\n";
        }
        my $date_rx=qr/\d\d\d\d-\d\d-\d\d/;
        my $time_rx=qr/\d\d:\d\d:\d\d,\d\d\d/;
        my $level_rx=qr/DEBUG|INFO|WARN|ERROR/;
        my $where_rx=qr/\[.*?\]/;
        my $msg_rx=qr/.*$/;
        $line =~ m/^($date_rx) ($time_rx) ($level_rx)  ($where_rx) ($msg_rx)/;
        $csv->combine($1, $2, $3, $4, $5);
        $ret.= $csv->string;
    }
}

1;
