#
# Copyright 2022 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

package notification::sirportly::mode::ticket;

use base qw(centreon::plugins::mode);

use strict;
use warnings;
use centreon::plugins::http;
use centreon::plugins::statefile;
use JSON::XS;

sub new {
    my ($class, %options) = @_;
    my $self = $class->SUPER::new(package => __PACKAGE__, %options);
    bless $self, $class;

    $options{options}->add_options(arguments => {
        'hostname:s'            => { name => 'hostname', default => 'api.sirportly.com' },
        'port:s'                => { name => 'port', default => 443 },
        'proto:s'               => { name => 'proto', default => 'https' },
        'api-token:s'           => { name => 'api_token' },
        'api-secret:s'          => { name => 'api_secret' },
        'cache'                 => { name => 'cache' },
        'contact-name:s'        => { name => 'contact_name', default => 'Centreon Notifications' },
        'contact-type:s'        => { name => 'contact_type', default => 'telephone' },
        'contact-data:s'        => { name => 'contact_data', default => '-' },
        'brand:s'               => { name => 'brand' },
        'department:s'          => { name => 'department' },
        'submit-user:s'         => { name => 'submit_user' },
        'close-user:s'          => { name => 'close_user' },
        'submit-status:s'       => { name => 'submit_status' },
        'close-status:s'        => { name => 'close_status' },
        'priority-mapping:s'    => { name => 'priority_mapping' },
        'priority:s'            => { name => 'priority' },
        'item-id:s'             => { name => 'item_id' },
        'subject:s'             => { name => 'subject' },
        'message:s'             => { name => 'message' },
        'timeout:s'             => { name => 'timeout' },
    });

    $self->{http} = centreon::plugins::http->new(%options);
    $self->{cache} = centreon::plugins::statefile->new(%options);

    return $self;
}

sub check_options {
    my ($self, %options) = @_;

    $self->SUPER::init(%options);
    foreach (('api_token', 'api_secret', 'brand', 'department', 'submit_status',
        'close_status', 'priority', 'item_id', 'subject', 'message')) {
        if (!defined($self->{option_results}->{$_})) {
            my $option = $_;
            $option =~ s/_/-/g;
            $self->{output}->add_option_msg(short_msg => "You need to set --$option option");
            $self->{output}->option_exit();
        }
    }

    $self->{http}->set_options(%{$self->{option_results}});
    $self->{cache}->check_options(option_results => $self->{option_results});
}

sub proceed {
    my ($self, %options) = @_;

    # Let's compute a priority mapping hash, with lc keys
    my %priority_mapping;
    if (defined($self->{option_results}->{priority_mapping})) {
        my $i = 0;
        %priority_mapping = map { $i++ % 2 ? $_ : lc } split /[:,]/, $self->{option_results}->{priority_mapping};
    }

    # Let's compute a ticket tag to be able to find the opened ticket later on
    my $tag = $options{item_id};
    if ($options{priority} =~ /^(UP|DOWN|UNREACHABLE)$/i) {
        $tag = "centreon-hst:$tag";
    } else {
        $tag = "centreon-svc:$tag";
    }

    # Is there an existing Sirportly ticket for this item ?
    my $post_param = [
        "spql=SELECT tickets.reference,statuses.name FROM tickets WHERE ticket_tags.tag=\"$tag\" AND statuses.name!=\"$self->{option_results}->{close_status}\" ORDER BY tickets.submitted_at DESC LIMIT 1",
    ];
    my $response = $self->{http}->request(method => 'POST', url_path =>'/api/v2/tickets/spql', post_param => $post_param, warning_status => '', unknown_status => '', critical_status => '');
    my $ticket_info;
    eval {
        $ticket_info = JSON::XS->new->decode($response)->{results}[0];
    };

    if ($options{priority} =~ /^(UP|OK)$/i) { # Close the ticket
        if (defined($ticket_info)) {
            $post_param = [
                "ticket=$ticket_info->[0]",
                "contact=$self->{option_results}->{contact_name}",
                "message=$options{message}",
            ];
            $self->{http}->request(method => 'POST', url_path =>'/api/v2/tickets/post_update', post_param => $post_param, warning_status => '', unknown_status => '', critical_status => '');
            if ($ticket_info->[1] eq $self->{option_results}->{submit_status}) {
                $post_param = [
                    "ticket=$ticket_info->[0]",
                    "user=" . (defined($self->{option_results}->{close_user}) ? $self->{option_results}->{close_user} : ""),
                    "status=$self->{option_results}->{close_status}",
                ];
                my $response = $self->{http}->request(method => 'POST', url_path =>'/api/v2/tickets/update', post_param => $post_param, warning_status => '', unknown_status => '', critical_status => '');
                eval {
                    JSON::XS->new->decode($response);
                };
                if ($@) { # We only check we received a response from Sirportly, thus we don't take the risk to hold the queue/cache processing
                    $ticket_info->[0] = "failed";
                }
            }
        }
    } elsif (defined($ticket_info)) { # Update an existing ticket
        $post_param = [
            "ticket=$ticket_info->[0]",
            "contact=$self->{option_results}->{contact_name}",
            "message=$options{message}",
        ];
        $self->{http}->request(method => 'POST', url_path =>'/api/v2/tickets/post_update', post_param => $post_param, warning_status => '', unknown_status => '', critical_status => '');
        $post_param = [
            "ticket=$ticket_info->[0]",
            "priority=" . (defined($priority_mapping{lc($options{priority})}) ? $priority_mapping{lc($options{priority})} : $options{priority}),
        ];
        my $response = $self->{http}->request(method => 'POST', url_path =>'/api/v2/tickets/update', post_param => $post_param, warning_status => '', unknown_status => '', critical_status => '');
        eval {
            JSON::XS->new->decode($response);
        };
        if ($@) { # We only check we received a response from Sirportly, thus we don't take the risk to hold the queue/cache processing
            $ticket_info->[0] = 'failed';
        }
    } else { # Open a new ticket
        $post_param = [
            "contact_name=$self->{option_results}->{contact_name}",
            "contact_method_type=$self->{option_results}->{contact_type}",
            "contact_method_data=$self->{option_results}->{contact_data}",
            "brand=$self->{option_results}->{brand}",
            "department=$self->{option_results}->{department}",
            "user=" . (defined($self->{option_results}->{submit_user}) ? $self->{option_results}->{submit_user} : ""),
            "status=$self->{option_results}->{submit_status}",
            "priority=" . (defined($priority_mapping{lc($options{priority})}) ? $priority_mapping{lc($options{priority})} : $options{priority}),
            "tag_list=$tag",
            "subject=$options{subject}",
            "message=$options{message}",
        ];
        my $response = $self->{http}->request(method => 'POST', url_path =>'/api/v2/tickets/submit', post_param => $post_param, warning_status => '', unknown_status => '', critical_status => '');
        my $decoded;
        eval {
            $decoded = JSON::XS->new->decode($response);
        };
        if ($@ || !defined($decoded->{reference})) { # Here we check Sirportly correctly received the new ticket, as it's the most important step
            $ticket_info->[0] = "failed";
        } else {
            $ticket_info->[0] = $decoded->{reference};
        }
    }

    # Return proceeded ticket
    return $ticket_info->[0];
}

sub run {
    my ($self, %options) = @_;

    # Authentication headers
    $self->{http}->add_header(key => 'X-Auth-Token', value => $self->{option_results}->{api_token});
    $self->{http}->add_header(key => 'X-Auth-Secret', value => $self->{option_results}->{api_secret});

    # Load cache
    $self->{cache}->read(statefile => 'sirportly_api_' . $self->{option_results}->{api_token});
    my $cache;
    if (defined($self->{option_results}->{cache})) {
        $cache = $self->{cache}->get(name => 'cache');
    } else {
        $cache = {};
    }

    # Add current item to cache
    my $cache_index = time();
    $cache->{$cache_index}->{item_id} = $self->{option_results}->{item_id};
    $cache->{$cache_index}->{priority} = $self->{option_results}->{priority};
    $cache->{$cache_index}->{subject} = $self->{option_results}->{subject};
    $cache->{$cache_index}->{message} = $self->{option_results}->{message};
    $self->{cache}->write(data => {cache => $cache});

    # Proceed cached notifications
    my $cache_size = keys %{$cache};
    my $cache_done = 0;
    foreach $cache_index (sort keys %{$cache}) {
        my $ticket_reference = $self->proceed(
            priority => $cache->{$cache_index}->{priority},
            item_id  => $cache->{$cache_index}->{item_id},
            subject  => $cache->{$cache_index}->{subject},
            message  => $cache->{$cache_index}->{message},
        );
        if (!defined($ticket_reference) || $ticket_reference ne 'failed') {
            delete($cache->{$cache_index});
            $self->{cache}->write(data => {cache => $cache});
            $cache_done++;
        } else {
            last;
        }
    }

    # Exit
    $self->{output}->output_add(
        severity => ($cache_size == $cache_done) ? 'OK' : 'CRITICAL',
        short_msg => "Proceeded " . $cache_done . "/" . $cache_size . " notifications"
    );
    $self->{output}->display(force_ignore_perfdata => 1);
    $self->{output}->exit();
}

1;

__END__

=head1 MODE

Open tickets via Sirportly API (https://sirportly.com/docs/api-specification/)

=over 6

=item B<--hostname>

Hostname of the OVH SMS API (default: 'api.sirportly.com').

=item B<--port>

Port used by API (default: '443').

=item B<--proto>

Specify https if needed (Default: 'https').

=item B<--api-token>

Specify the API authentication token.

=item B<--api-secret>

Specify the API authentication secret.

=item B<--cache>

Cache notifications not correctly delivered, to retry at next run.

=item B<--contact-name>

Specify the contact name for this ticket (default: 'Centreon Notifications').

=item B<--contact-type>

Specify the contact type (email or telephone) of the contact (default: 'telephone').

=item B<--contact-data>

Specify the email address or telephone number of the contact (default: '-').

=item B<--brand>

Specify the brand for this ticket.

=item B<--department>

Specify the department for this ticket.

=item B<--submit-user>

Specify the user to assign the ticket to when submitting the ticket.

=item B<--close-user>

Specify the user to assign the ticket to when closing the ticket.

=item B<--submit-status>

Specify the Sirportly status to be used when submitting the ticket.

=item B<--close-status>

Specify the Sirportly status to be used when closing the ticket (only if still in submit status).

=item B<--priority-mapping>

Map the Centreon priorities to Sirportly ones, comma separated (syntax: (down|unreachable|warning|critical|unknown):priority).

=item B<--priority>

Specify the priority for this ticket.

=item B<--item-id>

Specify the host or service ID for this ticket.

=item B<--subject>

Specify the subject for this ticket.

=item B<--message>

Specify the message for this ticket.

=item B<--timeout>

Threshold for HTTP timeout

=back

=cut
