package AccountManager::App::Step3;

use Mojo::Base qw(AccountManager::App::Controller);

use DateTime;
use Email::MIME;
use Email::Sender::Simple;
use English qw(-no_match_vars);
use Syntax::Keyword::Try;
use Template::Constants qw(:chomp);

use AccountManager::Token;
use AccountManager::Tools;

sub run {
    my $self = shift;

    my $config = $self->app()->config();
    my $log    = $self->app()->log();

    $self->init_db();
    $self->init_l10n();
    $self->init_user();

    if ($config->{app}->{login_url}) {
        return if !$self->check_authentication();
    }

    my $entityid = $self->param('entityid');
    my $email    = $self->param('email');
    my $db       = $self->stash('db');
    my $l10n     = $self->stash('l10n');
    my $user     = $self->stash('user');

    my $sp = $self->get_sp(entityid => $entityid);
    return if !$sp;

    # override metadata contacts if needed
    my $contacts =
        $config->{$entityid}->{contacts} ||
        $config->{service}->{contacts};
    if ($contacts) {
        if ($contacts =~ /^\+(.+)/) {
            # complement original contacts
            $sp->contacts($sp->contacts(), split(/, */, $1));
        } else {
            # replace original contacts
            $sp->contacts(split(/, */, $contacts));
        }
    }

    ## Check that email is a known contact for this SP
    return $self->abort(
        log_message  => "Requested a token for SP $entityid with unautorized address $email",
        user_message => "internal",
    ) if !$sp->is_contact($email);

    # delete any previous token for the same email/service couple
    my $old_token = AccountManager::Token->new(
        db            => $db,
        email_address => $email,
        entityid      => $entityid,
    );

    if ($old_token->load(speculative => 1)) {
        try {
            $old_token->delete();
        } catch  {
            return $self->abort(
                log_message  => "Failed to delete old authentication token",
                user_message => "internal"
            );
        }
    }

    # compute a new token
    my $validity_period =
        $config->{service}->{tokens_validity_period};
    my $token = AccountManager::Token->new(
        db              => $db,
        email_address   => $email,
        entityid        => $entityid,
        creation_date   => DateTime->now(),
        expiration_date => DateTime->now()->add(hours => $validity_period),
        secret          => AccountManager::Tools::generate_secret(20)
    );

    try {
        $token->save();
    } catch {
        return $self->abort(
            log_message  => "Failed to save creation authentication token",
            user_message => "internal"
        );
    }

    # build content
    my $theme              = $config->{setup}->{templates_theme} || 'default';
    my $base_templates_dir = $self->app()->home()->child('templates');
    my $tt2 = Template->new({
        ENCODING     => 'utf8',
        PRE_CHOMP    => CHOMP_ONE,
        INCLUDE_PATH => [
            $base_templates_dir->child('mail', $theme),
            $base_templates_dir->child('mail'),
        ]
    });

    my $data = {
        app => {
            url           => $config->{app}->{url},
            support_email => $config->{app}->{support_email},
            version       => $config->{app}->{version},
            name          => $config->{app}->{name},
        },
        user          => $user->{name},
        source_ip     => $self->client_ip(),
        idp           => { entityid => $user->{idp}, },
        sp            => { entityid => $entityid, },
        to            => $email,
        token         => $token->secret(),
        challenge_url => $self->url_for('step3')->query(entityid => $entityid, email => $email)->to_abs(),
        lh            => $l10n
    };
    my $text_content;
    my $html_content;
    $tt2->process('send_authentication_token.tt2.txt',  $data, \$text_content);
    $tt2->process('send_authentication_token.tt2.html', $data, \$html_content);

    my $message = Email::MIME->create(
        header_str => [
            'From'         => sprintf('%s <%s>', $config->{app}->{name}, $config->{mailer}->{from}),
            'To'           => $email,
            'Subject'      => sprintf('[%s] %s', $config->{app}->{name}, $l10n->maketext("Test accounts request")),
            'Content-Type' => 'multipart/alternative'
        ],
        parts => [
            Email::MIME->create(
                attributes => {
                    content_type => "text/plain",
                    charset      => 'utf-8',
                    encoding     => 'quoted-printable'
                },
                body_str => $text_content
            ),
            Email::MIME->create(
                attributes => {
                    content_type => "text/html",
                    charset      => 'utf-8',
                    encoding     => 'quoted-printable'
                },
                body_str => $html_content
            ),
        ]
    );

    try {
        local $ENV{PATH} = '/bin:/sbin:/usr/bin:/usr/sbin';
        Email::Sender::Simple->send($message);
    } catch($error) {
        return $self->abort(
            log_message  => "Mail notification error: $error",
            user_message => "mail_notification_failure"
        );
    }

    $log->info(
        sprintf(
            "Token send to %s for entityid=%s;token=%s",
            $email,
            $entityid,
            $token->secret(),
        )
    );

    my $profiles = $base_templates_dir
        ->child('accounts')
        ->list()
        ->map(sub { m/([^\/]+).tt2$/})
        ->to_array();

    $self->stash(entityid => $entityid);
    $self->stash(email    => $email);
    $self->stash(validity => $config->{service}->{account_validity_period});
    $self->stash(profiles => $profiles);

    $self->render(
        status   => 200,
        template => 'step3',
        format   => 'html'
    );
}

1;