#!/usr/bin/perl
use strict;
use warnings;
use English;
use Getopt::Long;
use Pod::Usage;
use NGCP::API::Client;
use Readonly;
use XML::Simple;
use Template;
use Email::Sender::Simple qw();
use Email::Simple;
use Email::Simple::Creator;
use Email::Sender::Transport::Sendmail qw();

Readonly my @required => qw();
Readonly my $config_file => '/etc/ngcp-panel/provisioning.conf';
Readonly my $interval => 'month';

my $opts = {
    verbose => 0,
};

my $config;

GetOptions( $opts,
    "help|h" => \&usage,
    "verbose",
) or usage();

sub check_params {
    my @missing;
    foreach my $param (@required) {
        push @missing, $param unless $opts->{$param};
    }
    usage(join(' ', @missing)) if scalar @missing;
    return;
}

sub usage {
    my $msg = shift;
    pod2usage(-exitval => $msg ? 1 : 0,
              -verbose => 99,
              -sections => [ qw(NAME OPTIONS USAGE) ],
              -message => $msg
                            ? $msg =~ /not found/i
                                ? $msg
                                : "Missing parameters: $msg"
                            : '',
             );
    return;
}

sub load_config {
    $config = XML::Simple->new()->XMLin($config_file, ForceArray => 0)
        or die "Cannot load config: $config_file: $ERRNO";
    return;
}

sub get_data {
    my ($uri, $link) = @_;
    my $client = new NGCP::API::Client;
    $client->set_verbose($opts->{verbose});
    $client->set_page_rows(10);
    my @result = ();
    while (my $res = $client->next_page($uri)) {
        die $res->result unless $res->is_success;
        my $res_hash = $res->as_hash;
        my $data = $res_hash->{_embedded}{'ngcp:'.$link};
        if ('ARRAY' eq ref $data) {
            push(@result,@$data);
        } elsif ($data) {
            push(@result,$data);
        }
    }
    return \@result;
}

sub get_email_template {
    my $event = shift;
    my $lock_type = $event->{interval_lock} ? 'lock' : 'warning';
    my $reseller_id = $event->{reseller_id};
    my @templates_data = ();
    foreach my $reseller ($event->{reseller_id}, 'NULL') {
        @templates_data = (
            @templates_data,
            @{get_data(sprintf('/api/emailtemplates/?reseller_id=%s', $reseller),
                       'emailtemplates')});
    }
    my $selected_template;
    foreach my $template (@templates_data) {
        next if $template->{name} ne 'customer_fraud_'.$lock_type.'_default_email'
             && $template->{name} ne 'customer_fraud_'.$lock_type.'_email';
        next if $template->{reseller_id} && $template->{reseller_id} != $reseller_id;
        $selected_template = $template;
        last if $template->{reseller_id};
    }

    unless ($selected_template) {
        die sprintf "No template 'customer_fraud_%s_default_email' OR 'customer_fraud_%s_email' is defined.", $lock_type, $lock_type;
    }

    return $selected_template;
}

sub send_email {
    my ($event, $subscribers) = @_;

    my $template = get_email_template($event);

    my $vars = { adminmail => $config->{adminmail},
                 customer_id => $event->{id},
                 interval => $event->{interval},
                 interval_cost  => sprintf('%.2f', $event->{interval_cost}/100),
                 interval_limit => sprintf('%.2f', $event->{interval_limit}/100),
                 type => $event->{type} eq 'profile_limit'
                            ? 'billing profile' : 'customer',
               };

    foreach my $subscriber (@{$subscribers}) {
        $vars->{subscribers} .= sprintf "%s\@%s %s\n",
                                @{$subscriber}{qw(username domain)},
                                $subscriber->{external_id}
                                    ? '('.$subscriber->{external_id}.')' : '';
    }

    my $tt = Template->new();
    map { my $out;
          $tt->process(\$template->{$_}, $vars, \$out);
          $out and $template->{$_} = $out;
        } keys %{$template};

    die "'To' header is empty in the email" unless $event->{interval_notify};
    die "'From' header is empty in the email" unless $template->{from_email};
    die "'Subject' header is empty in the email" unless $template->{subject};

    my $transport = Email::Sender::Transport::Sendmail->new;
    my $email = Email::Simple->create(
        header => [
            To      => $event->{interval_notify},
            From    => $template->{from_email},
            Subject => $template->{subject},
        ],
        body => $template->{body},
    );

    Email::Sender::Simple->send($email, { transport => $transport })
        or die sprintf "Cannot send fraud auto lock notification to %s: $ERRNO",
                       $email->header('To');

    return;
}


sub lock_customer {
    my ($event, $subscribers) = @_;

    my $client = new NGCP::API::Client;
    $client->set_verbose($opts->{verbose});
    my $uri = '/api/customers/'.$event->{id};
    my $data = [ { op => 'replace',
                   path => '/status',
                   value => 'locked' } ];
    my $res = $client->request("PATCH", $uri, $data);
    if ($res->status_line =~ /200 OK/) {
        foreach my $subscriber (@{$subscribers}) {
            $uri = '/api/subscribers/'.$subscriber->{id};
            $data = [ { op => 'replace',
                        path => '/status',
                        value => 'locked' },
                      { op => 'replace',
                        path => '/lock',
                        value => $event->{interval_lock} } ];
            $res = $client->request("PATCH", $uri, $data);
        }
    }

    return;
}

sub main {
    check_params();
    load_config();
    my $events = get_data(sprintf('/api/customerfraudevents/?interval=%s',
                                  $interval),
                                  'customerfraudevents');
    foreach my $event (@{$events}) {
        my $subscribers = get_data(sprintf('/api/subscribers/?customer_id=%d',
                                   $event->{id}),
                                   'subscribers');
        $event->{interval} = 'month';
        if (defined $event->{interval_lock} && $event->{interval_lock} > 0) {
            lock_customer($event, $subscribers);
        }
        if ($event->{interval_notify}) {
            eval {
                send_email($event, $subscribers);
            };
        }
        print $EVAL_ERROR if $EVAL_ERROR;
    }
    return;
}

main();

exit 0;

__END__

=head1 NAME

ngcp-fraud-auto-lock - checks for contract balances above fraud limit thresholds

=head1 SYNOPSIS

ngcp-fraud-daily-lock

=head1 OPTIONS

=over 8

=item B<-help>

Print a brief help message.

=item B<-verbose>

Show additional debug information. Default false.

=back

=head1 USAGE

ngcp-fraud-auto-lock [options]

=head1 DESCRIPTION

B<This program> checks for contract balances above fraud limit warning thresholds and sends email notifications about the incidents as applying actions on such contracts (e.g. lock). Interval: 'month'

=head1 REQUIRED ARGUMENTS

None

=head1 EXIT STATUS

Exit code 0 means everything is ok otherwise 1.

=head1 CONFIGURATION

=head1 DIAGNOSTICS

=head1 SEE ALSO

NGCP::API::Client

=head1 DEPENDENCIES

ngcp-fraud-auto-lock relies on a bunch of Perl modules, all of them specified as
dependencies through the Debian package.

=head1 INCOMPATIBILITIES

No known at this time.

=head1 BUGS AND LIMITATIONS

Please report problems you notice to the Sipwise
Development Team <support@sipwise.com>.

=head1 AUTHOR

Kirill Solomko <ksolomko@sipwise.com>

=head1 COPYRIGHT

Copyright (C) 2016 Sipwise GmbH, Austria

=head1 LICENSE

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

=cut
