#! /usr/bin/perl
#
# Copyright (C) 2012-2021 Alexis Bienvenüe <paamc@passoire.fr>
#
# This file is part of Auto-Multiple-Choice
#
# Auto-Multiple-Choice 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 2 of
# the License, or (at your option) any later version.
#
# Auto-Multiple-Choice 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 Auto-Multiple-Choice.  If not, see
# <http://www.gnu.org/licenses/>.

use warnings;
use 5.012;

use Getopt::Long;

use AMC::Basic;
use AMC::NamesFile;
use AMC::Data;
use AMC::DataModule::report ':const';
use AMC::Gui::Avancement;
use AMC::Substitute;

use Module::Load;

use Email::MIME;
use Email::Address;
use Email::Sender;
use Email::Sender::Simple qw(sendmail);

use Time::HiRes qw( usleep );

my $project_dir       = '';
my $data_dir          = '';
my $students_list     = '';
my $list_encoding     = 'UTF-8';
my $csv_build_name    = '';
my $ids_file          = '';
my $email_column      = '';
my $sender            = '';
my $transport         = 'sendmail';
my $sendmail_path     = '/usr/sbin/sendmail';
my $smtp_host         = 'smtp';
my $smtp_port         = 25;
my $smtp_ssl          = '';
my $smtp_user         = '';
my $smtp_passwd_file  = '';
my $debug_smtp        = $ENV{AMC_DEBUG_SMTP};
my $text              = '';
my $text_content_type = 'text/plain';
my $subject           = '';
my $project_name      = '';
my $cc                = '';
my $bcc               = '';
my $log_file          = '';
my $delay_s           = 0;
my @attach_files      = ();
my $progress          = '';
my $progress_id       = '';
my $report_kind       = REPORT_ANNOTATED_PDF;
my $preassoc_key      = '';

unpack_args();
my @ARGV_ORIG = @ARGV;

GetOptions(
    "project=s"           => \$project_dir,
    "project-name=s"      => \$project_name,
    "data=s"              => \$data_dir,
    "students-list=s"     => \$students_list,
    "preassoc-key=s"      => \$preassoc_key,
    "list-encoding=s"     => \$list_encoding,
    "csv-build-name=s"    => \$csv_build_name,
    "ids-file=s"          => \$ids_file,
    "report=s"            => \$report_kind,
    "email-column=s"      => \$email_column,
    "sender=s"            => \$sender,
    "text=s"              => \$text,
    "text-content-type:s" => \$text_content_type,
    "subject=s"           => \$subject,
    "transport=s"         => \$transport,
    "sendmail-path=s"     => \$sendmail_path,
    "smtp-host=s"         => \$smtp_host,
    "smtp-port=s"         => \$smtp_port,
    "smtp-ssl=s"          => \$smtp_ssl,
    "smtp-user=s"         => \$smtp_user,
    "smtp-passwd-file=s"  => \$smtp_passwd_file,
    "debug-smtp!"         => \$debug_smtp,
    "progression=s"       => \$progress,
    "progression-id=s"    => \$progress_id,
    "attach=s"            => \@attach_files,
    "delay=s"             => \$delay_s,
    "log=s"               => \$log_file,
    "cc=s"                => \$cc,
    "bcc=s"               => \$bcc,
);

debug "Parameters: " . join( " ", map { "<$_>" } @ARGV_ORIG );

my $report_filename =
    {
     &REPORT_ANNOTATED_PDF => "corrected.pdf",
     &REPORT_PRINTED_COPY => "subject.pdf",
    }->{$report_kind} || "file.pdf";

my $via_preassoc = ( $report_kind == REPORT_PRINTED_COPY );

sub error {
    my ($text) = @_;
    debug "AMC-sendmail ERROR: $text";
    print "ERROR: $text\n";
    exit(1);
}

sub parse_add {
    my ($s) = @_;
    return ( map { $_->address(); } ( Email::Address->parse($s) ) );
}

$data_dir = "$project_dir/data" if ( $project_dir && !$data_dir );

error("students list not found:$students_list") if ( !-f $students_list );

my $students = AMC::NamesFile::new(
    $students_list,
    encodage    => $list_encoding,
    identifiant => $csv_build_name
);

error("data directory not found: $data_dir") if ( !-d $data_dir );

my %ids = ();
if ( -f $ids_file ) {
    debug "provided IDS:";
    open( IDS, $ids_file );
    while (<IDS>) {
        chomp;
        debug "[$_]";
        $ids{$_} = 1;
    }
    close(IDS);
} else {
    debug "IDS file $ids_file not found";
}

if ($log_file) {
    open( LOGF, ">>", $log_file )
      or debug "Error opening log file $log_file: $!";
    print LOGF localtime . " Starting mailing...\n";
}

my $avance = AMC::Gui::Avancement::new( $progress, id => $progress_id );

my $data    = AMC::Data->new($data_dir);
my $layout  = $data->module('layout');
my $report  = $data->module('report');
my $assoc   = $data->module('association');
my $scoring = $data->module('scoring');

my $subst = AMC::Substitute::new(
    assoc   => $assoc,
    scoring => $scoring,
    names   => $students,
    name    => $project_name
);

$data->begin_read_transaction('Mail');
my $subdir = $report->get_dir($report_kind);

my $pdf_dir = "";
if($subdir) {
    $pdf_dir = "$project_dir/$subdir";
}

error("PDF directory not found: $pdf_dir") if ( $pdf_dir && !-d $pdf_dir );

my ($key, $r);
if ( $via_preassoc ) {
    $key = $preassoc_key;
    $r = $report->get_preassociated_type($report_kind);
} else {
    $key = $assoc->variable('key_in_list');
    $r = $report->get_associated_type($report_kind);
}

my $t;
if ( $transport eq 'sendmail' ) {
    load Email::Sender::Transport::Sendmail;
    $t =
      Email::Sender::Transport::Sendmail->new( { sendmail => $sendmail_path } );
} elsif ( $transport eq 'SMTP' ) {
    my $pass = '';
    if ( $smtp_user && -f $smtp_passwd_file ) {
        $pass = file_content($smtp_passwd_file);
        $pass =~ s/\n.*//s;
    }
    load Email::Sender::Transport::SMTP;
    $t = Email::Sender::Transport::SMTP->new(
        {
            host          => $smtp_host,
            port          => $smtp_port,
            ssl           => $smtp_ssl,
            sasl_username => $smtp_user,
            sasl_password => $pass,
            debug         => $debug_smtp,
        }
    );
} else {
    error("Unknown transport: $transport");
}

my $nn = 1 + $#$r;
if ($ids_file) {
    my @i = ( keys %ids );
    $nn = 1 + $#i;
}
my $delta = ( $nn > 0 ? 1 / $nn : 1 );

my @attachments = ();

for my $f (@attach_files) {
    if ( -f $f ) {
        my $name = $f;
        $name =~ s/.*\///;
        my $body = '';
        open( ATT, $f );
        while (<ATT>) { $body .= $_; }
        close(ATT);
        push @attachments,
          Email::MIME->create(
            attributes => {
                filename     => $name,
                content_type => file_mimetype($f),
                encoding     => "base64",
                name         => $name,
                disposition  => "attachment",
            },
            body => $body,
          );
    } else {
        debug "ERROR: Cannot attach inexistant file $f";
    }
}

my $failed_auth = 0;

STUDENT: for my $i (@$r) {
    my ($s) = $students->data( $key, $i->{id}, test_numeric => 1 );
    my $dest = $s->{$email_column};
    debug "Loop: ID $i->{id} DEST [$dest]";
    if ( $ids_file && !$ids{ $i->{id} } ) {
        debug "Skipped";
        next STUDENT;
    }
    if ($failed_auth) {
        print "FAILED: auth\n";
        next STUDENT;
    }
    if ($dest) {
        my $file = $i->{file};

        if ($pdf_dir) {
            $file = $pdf_dir . "/$file";
        }

        debug "  FILE=$file";
        if ( -f $file ) {
            my $body = '';
            $body = file_content($file);

            my @sc    = ( $i->{student}, $i->{copy} );
            my @parts = (
                Email::MIME->create(
                    attributes => {
                        content_type => $text_content_type,
                        encoding     => "base64",
                        charset      => "UTF-8",
                    },
                    body_str => $subst->substitute( $text, @sc ),
                ),
                Email::MIME->create(
                    attributes => {
                        filename     => $report_filename,
                        content_type => "application/pdf",
                        encoding     => "base64",
                        name         => $report_filename,
                        disposition  => "attachment",
                    },
                    body => $body,
                ),
            );

            my $email = Email::MIME->create(
                header_str => [
                    From    => $sender,
                    To      => $dest,
                    Subject => $subst->substitute( $subject, @sc ),
                    "User-Agent" =>
"AutoMultipleChoice, version 1.5.2 (r:d5fe5a59)",
                    "X-Project"     => $project_name,
                    "X-AMC-Student" => $i->{id},

                ],
                parts => [ @parts, @attachments ],
            );
            $email->header_str_set( Cc => $cc ) if ($cc);

            my @all_dests = ( parse_add($dest) );
            push @all_dests, parse_add($cc)  if ($cc);
            push @all_dests, parse_add($bcc) if ($bcc);
            my $b = eval {
                sendmail( $email, { transport => $t, to => \@all_dests } );
            } || $@;

            my $status;
            my $m = '';

            if ( $b->isa('Email::Sender::Failure') ) {
                $status = 'FAILED';
                $m      = $b->message;
                $m =~ s/[\n\r]+/ | /g;
            } elsif ( $b->isa('Email::Sender::Success') ) {
                $status = 'OK';
            } else {
                $status = 'ERROR';
                $m      = $b;
            }

            # In case of failed authentication, cancel all sendings
            if ( $status eq 'FAILED' && $m =~ /failed auth/i ) {
                debug "Failed authentication: cancel all sendings";
                $failed_auth = 1;
            }

            print "$status [$i->{id}] $m\n";
            debug "$status [$i->{id}] $m";
            print LOGF "$status [$i->{id} -> $dest] $m\n" if ($log_file);

            next STUDENT if ($failed_auth);

            $report->report_mailing( $report_kind, @sc,
                ( $status eq 'OK' ? REPORT_MAIL_OK : REPORT_MAIL_FAILED ),
                $m, 'now' );
        } else {
            debug_and_stderr "No file: $file";
        }
        usleep( int( 1000000 * $delay_s ) );
    } else {
        debug "No dest";
    }
    $avance->progres($delta);
}

$data->end_transaction('Mail');

$avance->fin();

print "VAR: failed_auth=$failed_auth\n";

if ($log_file) {
    close(LOGF);
}
