# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --

package Kernel::System::Console::Command::Maint::Ticket::EscalationCheck;

use strict;
use warnings;

use List::Util qw(first);

use base qw(Kernel::System::Console::BaseCommand);

our @ObjectDependencies = (
    'Kernel::Config',
    'Kernel::System::Ticket',
    'Kernel::System::Time',
);

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

    $Self->Description('Triggers ticket escalation events and notification events for escalation.');

    return;
}

# =item Run()
#
# looks for the tickets which will escalate within the next five days
# then perform a search over the values on the TicketGet result
# for checking if any of the escalations values are present, then base
# on that the notification events for notifications and normal escalation
# events are trigger
#
# NotificationEvents:
#     - NotificationEscalation
#     - NotificationEscalationNotifyBefore
#
# Escalation events:
#     - EscalationResponseTimeStart
#     - EscalationUpdateTimeStart
#     - EscalationSolutionTimeStart
#     - EscalationResponseTimeNotifyBefore
#     - EscalationUpdateTimeNotifyBefore
#     - EscalationSolutionTimeNotifyBefore
#
#
# NotificationEvents are alway triggered, and Escalation events just
# base on the 'OTRSEscalationEvents::DecayTime'.
#
# =cut

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

    $Self->Print("<yellow>Processing ticket escalation events ...</yellow>\n");

    # get needed objects
    my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
    my $TimeObject   = $Kernel::OM->Get('Kernel::System::Time');

    # the decay time is configured in minutes
    my $DecayTimeInSeconds = $Kernel::OM->Get('Kernel::Config')->Get('OTRSEscalationEvents::DecayTime') || 0;
    $DecayTimeInSeconds *= 60;

    # check if it's a escalation or escalation notification
    # check escalation times
    my %TicketAttr2Event = (
        FirstResponseTimeEscalation   => 'EscalationResponseTimeStart',
        UpdateTimeEscalation          => 'EscalationUpdateTimeStart',
        SolutionTimeEscalation        => 'EscalationSolutionTimeStart',
        FirstResponseTimeNotification => 'EscalationResponseTimeNotifyBefore',
        UpdateTimeNotification        => 'EscalationUpdateTimeNotifyBefore',
        SolutionTimeNotification      => 'EscalationSolutionTimeNotifyBefore',
    );

    # Find all tickets which will escalate within the next five days.
    my @Tickets = $TicketObject->TicketSearch(
        Result                           => 'ARRAY',
        Limit                            => 100,
        TicketEscalationTimeOlderMinutes => -( 5 * 24 * 60 ),
        Permission                       => 'rw',
        UserID                           => 1,
    );

    TICKET:
    for my $TicketID (@Tickets) {

        # get ticket data
        my %Ticket = $TicketObject->TicketGet(
            TicketID      => $TicketID,
            DynamicFields => 0,
        );

        # get used calendar
        my $Calendar = $TicketObject->TicketCalendarGet(
            %Ticket,
        );

        # check if it is during business hours, then send escalation info
        my $CountedTime = $TimeObject->WorkingTime(
            StartTime => $TimeObject->SystemTime() - ( 10 * 60 ),
            StopTime  => $TimeObject->SystemTime(),
            Calendar  => $Calendar,
        );

        # don't trigger events if not counted time
        if ( !$CountedTime ) {
            next TICKET;
        }

        # needed for deciding whether events should be triggered
        my @HistoryLines = $TicketObject->HistoryGet(
            TicketID => $TicketID,
            UserID   => 1,
        );

        # check if it's a escalation of escalation notification
        # check escalation times
        my $EscalationType = 0;
        my @Events;
        TYPE:
        for my $Type (
            qw(FirstResponseTimeEscalation UpdateTimeEscalation SolutionTimeEscalation
            FirstResponseTimeNotification UpdateTimeNotification SolutionTimeNotification)
            )
        {
            next TYPE if !$Ticket{$Type};

            my @ReversedHistoryLines = reverse @HistoryLines;

            # get the last time this event was triggered
            # search in reverse order, as @HistoryLines sorted ascendingly by CreateTime
            if ($DecayTimeInSeconds) {

                my $PrevEventLine = first { $_->{HistoryType} eq $TicketAttr2Event{$Type} }
                @ReversedHistoryLines;

                if ( $PrevEventLine && $PrevEventLine->{CreateTime} ) {
                    my $PrevEventTime = $TimeObject->TimeStamp2SystemTime(
                        String => $PrevEventLine->{CreateTime},
                    );
                    my $TimeSincePrevEvent = $TimeObject->SystemTime() - $PrevEventTime;

                    next TYPE if $TimeSincePrevEvent <= $DecayTimeInSeconds;
                }
            }

            # emit the event
            push @Events, $TicketAttr2Event{$Type};

            if ( !$EscalationType ) {
                if ( $Type =~ /TimeEscalation$/ ) {
                    push @Events, 'NotificationEscalation';
                    $EscalationType = 1;
                }
                elsif ( $Type =~ /TimeNotification$/ ) {
                    push @Events, 'NotificationEscalationNotifyBefore';
                    $EscalationType = 1;
                }
            }
        }

        EVENT:
        for my $Event (@Events) {

            # trigger the event
            $TicketObject->EventHandler(
                Event  => $Event,
                UserID => 1,
                Data   => {
                    TicketID              => $TicketID,
                    CustomerMessageParams => {
                        TicketNumber => $Ticket{TicketNumber},
                    },
                },
                UserID => 1,
            );

            $Self->Print(
                "Ticket <yellow>$TicketID</yellow>: event <yellow>$Event</yellow>, processed.\n"
            );

            if ( $Event eq 'NotificationEscalation' || $Event eq 'NotificationEscalationNotifyBefore' ) {
                next EVENT;
            }

            # log the triggered event in the history
            $TicketObject->HistoryAdd(
                TicketID     => $TicketID,
                HistoryType  => $Event,
                Name         => "%%$Event%%triggered",
                CreateUserID => 1,
            );
        }
    }

    $Self->Print("<green>Done.</green>\n");
    return $Self->ExitCodeOk();
}

1;
