package Lemonldap::NG::Portal::2F::REST;

use strict;
use Mouse;
use Lemonldap::NG::Portal::Main::Constants qw(
  PE_BADOTP
  PE_ERROR
  PE_FORMEMPTY
  PE_OK
  PE_SENDRESPONSE
);

our $VERSION = '2.0.10';

extends qw(
  Lemonldap::NG::Portal::Main::SecondFactor
  Lemonldap::NG::Portal::Lib::REST
);

# INITIALIZATION

has prefix    => ( is => 'rw', default => 'rest' );
has initAttrs => ( is => 'rw', default => sub { {} } );
has vrfyAttrs => ( is => 'rw', default => sub { {} } );

sub init {
    my ($self) = @_;
    unless ( $self->conf->{rest2fVerifyUrl} ) {
        $self->logger->error('Missing REST verification URL');
        return 0;
    }
    $self->prefix( $self->conf->{sfPrefix} )
      if ( $self->conf->{sfPrefix} );
    foreach my $k ( keys %{ $self->conf->{rest2fInitArgs} } ) {
        my $attr = $self->conf->{rest2fInitArgs}->{$k};
        $attr =~ s/^$//;
        unless ( $attr =~ /^\w+$/ ) {
            $self->logger->error(
                "2F REST: $k key must point to a single attribute or macro");
            return 0;
        }
        $self->initAttrs->{$k} = $attr;
    }
    foreach my $k ( keys %{ $self->conf->{rest2fVerifyArgs} } ) {
        my $attr = $self->conf->{rest2fVerifyArgs}->{$k};
        $attr =~ s/^$//;
        unless ( $attr =~ /^\w+$/ ) {
            $self->logger->error(
                "2F REST: $k key must point to a single attribute or macro");
            return 0;
        }
        $self->vrfyAttrs->{$k} = $attr;
    }
    return $self->SUPER::init();
}

sub run {
    my ( $self, $req, $token ) = @_;

    my $checkLogins = $req->param('checkLogins');
    $self->logger->debug("REST2F: checkLogins set") if $checkLogins;

    my $stayconnected = $req->param('stayconnected');
    $self->logger->debug("REST2F: stayconnected set") if $stayconnected;

    if ( $self->conf->{rest2fInitUrl} ) {

        # Prepare args
        my $args = {};
        foreach my $k ( keys %{ $self->{initAttrs} } ) {
            $args->{$k} = $req->sessionInfo->{ $self->{initAttrs}->{$k} };
        }

        # Launch REST request
        $self->logger->debug('Call REST init URL');
        my $res =
          eval { $self->restCall( $self->conf->{rest2fInitUrl}, $args ); };
        if ($@) {
            $self->logger->error("REST 2F error: $@");
            return PE_ERROR;
        }
        unless ( $res->{result} ) {
            $self->logger->error("REST 2F initialization has failed");
            return PE_ERROR;
        }
    }
    else {
        $self->logger->debug('No init URL, skipping initialization');
    }

    # Prepare form
    my $tmp = $self->p->sendHtml(
        $req,
        'ext2fcheck',
        params => {
            MAIN_LOGO => $self->conf->{portalMainLogo},
            SKIN      => $self->p->getSkin($req),
            TOKEN     => $token,
            PREFIX    => $self->prefix,
            TARGET    => '/'
              . $self->prefix
              . '2fcheck?skin='
              . $self->p->getSkin($req),
            LEGEND        => 'enterRest2fCode',
            CHECKLOGINS   => $checkLogins,
            STAYCONNECTED => $stayconnected
        }
    );
    $self->logger->debug("Prepare external REST verification");

    $req->response($tmp);
    return PE_SENDRESPONSE;
}

sub verify {
    my ( $self, $req, $session ) = @_;
    my $code;
    unless ( $code = $req->param('code') ) {
        $self->userLogger->error('External REST 2F: no code');
        return PE_FORMEMPTY;
    }

    # Prepare args
    my $args = {};
    foreach my $k ( keys %{ $self->{vrfyAttrs} } ) {
        $args->{$k} = (
              $k eq 'code'
            ? $code
            : $session->{ $self->{vrfyAttrs}->{$k} }
        );
    }

    # Launch REST request
    $self->logger->debug('Call REST vrfy URL');
    my $res =
      eval { $self->restCall( $self->conf->{rest2fVerifyUrl}, $args ); };
    if ($@) {
        $self->logger->error("REST 2F error: $@");
        return PE_ERROR;
    }

    # Result
    unless ( $res->{result} ) {
        $self->userLogger->warn( 'REST Second factor failed for '
              . $session->{ $self->conf->{whatToTrace} } );
        return PE_BADOTP;
    }
    return PE_OK;
}

1;
