# --
# Modified version of the work: Copyright (C) 2006-2023 KIX Service Software GmbH, https://www.kixdesk.com
# based on the original work of:
# Copyright (C) 2001-2023 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file LICENSE for license information (AGPL). If you
# did not receive this file, see https://www.gnu.org/licenses/agpl.txt.
# --

package Kernel::Modules::AdminPerformanceLog;

use strict;
use warnings;

our $ObjectManagerDisabled = 1;

sub new {
    my ( $Type, %Param ) = @_;

    # allocate new hash for object
    my $Self = {%Param};
    bless( $Self, $Type );

    return $Self;
}

sub Run {
    my ( $Self, %Param ) = @_;

    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
    my $ConfigObject = $Kernel::OM->Get('Kernel::Config');

    # is performance log disabled?
    if ( !$ConfigObject->Get('PerformanceLog') ) {
        $LayoutObject->Block(
            Name => 'Disabled',
            Data => { %Param, },
        );

        # create & return output
        my $Output = $LayoutObject->Header();
        $Output .= $LayoutObject->NavigationBar();
        $Output .= $LayoutObject->Output(
            TemplateFile => 'AdminPerformanceLog',
            Data         => \%Param,
        );
        $Output .= $LayoutObject->Footer();
        return $Output;
    }

    $LayoutObject->Block(
        Name => 'Enabled',
        Data => { %Param, },
    );

    # reset log file
    if ( $Self->{Subaction} eq 'Reset' ) {

        # challenge token check for write action
        $LayoutObject->ChallengeTokenCheck();

        if ( !$Self->_DatabaseReset() ) {
            $LayoutObject->FatalError();
        }
        else {

            # redirect
            return $LayoutObject->Redirect(
                OP => "Action=$Self->{Action}",
            );
        }
    }

    # show detail view
    elsif ( $Self->{Subaction} eq 'View' ) {

        $LayoutObject->Block( Name => 'ActionList' );
        $LayoutObject->Block( Name => 'ActionOverview' );

        my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
        my %Action      = ();
        my $MaxRequest  = 0;
        my $Slot        = 60;
        my $MinuteSlot  = $ParamObject->GetParam( Param => 'Minute' );
        my $Interface   = $ParamObject->GetParam( Param => 'Interface' );
        my $Module      = $ParamObject->GetParam( Param => 'Module' );
        if ( $MinuteSlot < 31 ) {
            $Slot = 1;
        }
        elsif ( $MinuteSlot < 61 ) {
            $Slot = 2;
        }
        elsif ( $MinuteSlot < 121 ) {
            $Slot = 5;
        }
        elsif ( $MinuteSlot < 1141 ) {
            $Slot = 30;
        }
        my $Data = $Self->_DatabaseRead();
        $LayoutObject->Block(
            Name => 'View',
            Data => {
                Age => $LayoutObject->CustomerAge(
                    Age   => $MinuteSlot * 60,
                    Space => ' '
                ),
                Interface => $Interface || '-',
                Module    => $Module    || '-',
                Period    => $Slot,
            },
        );
        my $Minute = 0;
        my $Count  = 1;
        while ( $Count <= $MinuteSlot ) {
            ROW:
            for my $Row ( reverse @{$Data} ) {
                if (
                    $Row->[0] < ( time() - ( 60 * $Minute ) )
                    && $Row->[0] > ( time() - ( 60 * ( $Minute + $Slot ) ) )
                ) {

                    # for each action
                    my $ModuleCurrent = '';
                    if ( $Row->[4] =~ /^(.+?|)Action=(.+?)(&.*|)$/ ) {
                        $ModuleCurrent = $2;
                        if ( $Row->[4] =~ /Subaction=(.+?)(&.*|)$/ ) {
                            $ModuleCurrent .= '&' . $1;
                        }
                        if ($Interface) {
                            if ( !$Module && $Row->[1] ne $Interface ) {
                                next ROW;
                            }
                            if ( $Module && $Module ne $ModuleCurrent ) {
                                next ROW;
                            }
                        }

                        $Action{$Minute}->{Count}++;
                        if ( $MaxRequest < $Action{$Minute}->{Count} ) {
                            $MaxRequest = $Action{$Minute}->{Count};
                        }
                        if ( $Action{$Minute}->{Sum} ) {
                            $Action{$Minute}->{Sum} = $Action{$Minute}->{Sum} + $Row->[2];
                        }
                        else {
                            $Action{$Minute}->{Sum} = $Row->[2];
                        }
                        if ( !defined( $Action{$Minute}->{Max} ) ) {
                            $Action{$Minute}->{Max} = $Row->[2];
                        }
                        elsif ( $Action{$Minute}->{Max} < $Row->[2] ) {
                            $Action{$Minute}->{Max} = $Row->[2];
                        }
                        if ( !defined( $Action{$Minute}->{Min} ) ) {
                            $Action{$Minute}->{Min} = $Row->[2];
                        }
                        elsif ( $Action{$Minute}->{Min} > $Row->[2] ) {
                            $Action{$Minute}->{Min} = $Row->[2];
                        }
                    }
                }
                elsif ( $Row->[0] < ( time() - ( 60 * $Minute ) ) ) {
                    last ROW;
                }
            }
            $Minute = $Minute + $Slot;
            $Count  = $Count + $Slot;
        }
        $Minute = 0;
        $Count  = 1;
        while ( $Count <= $MinuteSlot ) {

            my $TimeObject = $Kernel::OM->Get('Kernel::System::Time');

            # set output class
            if ( $Action{$Minute} ) {
                my $Average = $Action{$Minute}->{Sum} / $Action{$Minute}->{Count};
                $Average =~ s/^(.*\.\d\d).+?$/$1/g;
                my $I = 100 / $MaxRequest;
                my $W = $Action{$Minute}->{Count} * $I || 1;

                $LayoutObject->Block(
                    Name => 'ViewRow',
                    Data => {
                        %{ $Action{$Minute} },
                        Average => $Average,
                        Date    => $TimeObject->SystemTime2TimeStamp(
                            SystemTime => $TimeObject->SystemTime() - $Minute * 60,
                        ),
                        Width => $W . "%",
                    },
                );
            }
            else {
                $LayoutObject->Block(
                    Name => 'ViewRow',
                    Data => {
                        Min     => 0,
                        Max     => 0,
                        Count   => $Action{$Minute}->{Count} || '0',
                        Average => 0,
                        Date    => $TimeObject->SystemTime2TimeStamp(
                            SystemTime => $TimeObject->SystemTime() - $Minute * 60,
                        ),
                        Width => '0%',
                    },
                );
            }
            $Minute = $Minute + $Slot;
            $Count  = $Count + $Slot;
        }

        # create & return output
        my $Output = $LayoutObject->Header();
        $Output .= $LayoutObject->NavigationBar();
        $Output .= $LayoutObject->Output(
            TemplateFile => 'AdminPerformanceLog',
            Data         => \%Param,
        );
        $Output .= $LayoutObject->Footer();
        return $Output;
    }

    # show overview
    # get avarage times
    my $Data = [];
    if ( $ConfigObject->Get('PerformanceLog') ) {

        # check file size
        if ( $Self->_DatabaseCheck() ) {
            $LayoutObject->Block(
                Name => 'Reset',
                Data => {
                    Size => sprintf "%.1f MBytes",
                    ( $Self->_DatabaseCheck() / ( 1024 * 1024 ) ),
                },
            );
            my $Output = $LayoutObject->Header();
            $Output .= $LayoutObject->NavigationBar();
            $Output .= $LayoutObject->Output(
                TemplateFile => 'AdminPerformanceLog',
                Data         => \%Param,
            );
            $Output .= $LayoutObject->Footer();
            return $Output;
        }
        else {
            $Data = $Self->_DatabaseRead();
        }
    }

    $LayoutObject->Block(
        Name => 'Overview',
    );

    for my $Minute ( 5, 30, 60, 2 * 60, 24 * 60, 2 * 24 * 60 ) {
        my %Count  = ();
        my %Action = ();
        my %Sum    = ();
        my %Max    = ();
        my %Min    = ();
        ROW:
        for my $Row ( reverse @{$Data} ) {
            if ( $Row->[0] > time() - ( 60 * $Minute ) ) {

                # whole
                $Count{ $Row->[1] }++;
                if ( $Sum{ $Row->[1] } ) {
                    $Sum{ $Row->[1] } = $Sum{ $Row->[1] } + $Row->[2];
                }
                else {
                    $Sum{ $Row->[1] } = $Row->[2];
                }
                if ( !defined( $Max{ $Row->[1] } ) ) {
                    $Max{ $Row->[1] } = $Row->[2];
                }
                elsif ( $Max{ $Row->[1] } < $Row->[2] ) {
                    $Max{ $Row->[1] } = $Row->[2];
                }
                if ( !defined( $Min{ $Row->[1] } ) ) {
                    $Min{ $Row->[1] } = $Row->[2];
                }
                elsif ( $Min{ $Row->[1] } > $Row->[2] ) {
                    $Min{ $Row->[1] } = $Row->[2];
                }

                # for each action
                if ( $Row->[4] =~ /^(.+?|)Action=(.+?)(&.*|)$/ ) {
                    my $Module = $2;
                    if ( $Row->[4] =~ /Subaction=(.+?)(&.*|)$/ ) {
                        $Module .= '&' . $1;
                    }
                    $Action{$Module}->{Count}->{ $Row->[1] }++;
                    if ( $Action{$Module}->{Sum}->{ $Row->[1] } ) {
                        $Action{$Module}->{Sum}->{ $Row->[1] } = $Action{$Module}->{Sum}->{ $Row->[1] } + $Row->[2];
                    }
                    else {
                        $Action{$Module}->{Sum}->{ $Row->[1] } = $Row->[2];
                    }
                    if ( !defined( $Action{$Module}->{Max}->{ $Row->[1] } ) ) {
                        $Action{$Module}->{Max}->{ $Row->[1] } = $Row->[2];
                    }
                    elsif ( $Action{$Module}->{Max}->{ $Row->[1] } < $Row->[2] ) {
                        $Action{$Module}->{Max}->{ $Row->[1] } = $Row->[2];
                    }
                    if ( !defined( $Action{$Module}->{Min}->{ $Row->[1] } ) ) {
                        $Action{$Module}->{Min}->{ $Row->[1] } = $Row->[2];
                    }
                    elsif ( $Action{$Module}->{Min}->{ $Row->[1] } > $Row->[2] ) {
                        $Action{$Module}->{Min}->{ $Row->[1] } = $Row->[2];
                    }
                }
            }
            else {
                last ROW;
            }
        }
        if (%Sum) {
            $LayoutObject->Block(
                Name => 'OverviewTable',
                Data => {
                    Age =>
                        $LayoutObject->CustomerAge(
                        Age   => $Minute * 60,
                        Space => ' '
                        ),
                },
            );
        }
        for my $Interface (qw(Agent Customer Public)) {
            if ( defined $Sum{$Interface} ) {

                my $Average = $Sum{$Interface} / $Count{$Interface};
                $Average =~ s/^(.*\.\d\d).+?$/$1/g;
                $LayoutObject->Block(
                    Name => 'OverviewInterface',
                    Data => {
                        Interface => $Interface,
                        Average   => $Average,
                        Count     => $Count{$Interface} || 0,
                        Minute    => $Minute,
                        Sum       => $Sum{$Interface} || '0',
                        Max       => $Max{$Interface} || '0',
                        Min       => $Min{$Interface} || '0',
                    },
                );
                for my $Module ( sort keys %Action ) {
                    if ( defined $Action{$Module}->{Sum}->{$Interface} ) {

                        my $ModuleAverage = $Action{$Module}->{Sum}->{$Interface}
                            / $Action{$Module}->{Count}->{$Interface};
                        $ModuleAverage =~ s/^(.*\.\d\d).+?$/$1/g;
                        $LayoutObject->Block(
                            Name => 'OverviewRow',
                            Data => {
                                Interface => $Interface,
                                Module    => $Module,
                                Average   => $ModuleAverage,
                                Minute    => $Minute,
                                Count     => $Action{$Module}->{Count}->{$Interface} || '0',
                                Sum       => $Action{$Module}->{Sum}->{$Interface} || '0',
                                Max       => $Action{$Module}->{Max}->{$Interface} || '0',
                                Min       => $Action{$Module}->{Min}->{$Interface} || '0',
                            },
                        );
                    }
                }
            }
        }
    }

    # create & return output
    my $Output = $LayoutObject->Header();
    $Output .= $LayoutObject->NavigationBar();
    $Output .= $LayoutObject->Output(
        TemplateFile => 'AdminPerformanceLog',
        Data         => \%Param,
    );
    $Output .= $LayoutObject->Footer();
    return $Output;
}

sub _DatabaseCheck {
    my ( $Self, %Param ) = @_;

    my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
    my $File         = $ConfigObject->Get('PerformanceLog::File');

    # check file size
    my $FileSize = -s $File;
    if ( $FileSize > ( 1024 * 1024 * $ConfigObject->Get('PerformanceLog::FileMax') ) ) {
        return $FileSize;
    }
    else {
        return;
    }
}

sub _DatabaseReset {
    my ( $Self, %Param ) = @_;

    my $File = $Kernel::OM->Get('Kernel::Config')->Get('PerformanceLog::File');
    if ( !$Kernel::OM->Get('Kernel::System::Main')->FileDelete( Location => $File ) ) {
        return;
    }
    else {
        return 1;
    }
}

sub _DatabaseRead {
    my ( $Self, %Param ) = @_;

    my @Data     = ();
    my $File     = $Kernel::OM->Get('Kernel::Config')->Get('PerformanceLog::File');
    my $ArrayRef = $Kernel::OM->Get('Kernel::System::Main')->FileRead(
        Location => $File,
        Mode     => 'utf8',     # optional - binmode|utf8
        Result   => 'ARRAY',    # optional - SCALAR|ARRAY
    );
    if ($ArrayRef) {
        for ( @{$ArrayRef} ) {
            my $Line = $_;
            my @Row = split( /::/, $Line );
            push( @Data, \@Row );
        }
    }
    return \@Data;
}

1;

=back

=head1 TERMS AND CONDITIONS

This software is part of the KIX project
(L<https://www.kixdesk.com/>).

This software comes with ABSOLUTELY NO WARRANTY. For details, see the enclosed file
LICENSE for license information (AGPL). If you did not receive this file, see

<https://www.gnu.org/licenses/agpl.txt>.

=cut
