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.15';

extends qw(
  Lemonldap::NG::Portal::Lib::Code2F
  Lemonldap::NG::Portal::Lib::REST
);

# INITIALIZATION

# Prefix can overriden by sfExtra and is used for routes
has prefix => ( is => 'rw', default => 'rest' );

# Type is used to lookup config
has type => ( is => 'ro', default => 'rest' );

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

sub init {
    my ($self) = @_;

    if ( $self->code_activation ) {
        unless ( $self->conf->{rest2fInitUrl} ) {
            $self->logger->error('Missing REST intialization URL');
            return 0;
        }
    }
    else {
        unless ( $self->conf->{rest2fVerifyUrl} ) {
            $self->logger->error('Missing REST verification URL');
            return 0;
        }
    }

    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;
    }

    $self->prefix( $self->conf->{sfPrefix} ) if ( $self->conf->{sfPrefix} );
    return $self->SUPER::init();
}

sub sendCode {
    my ( $self, $req, $sessionInfo, $code ) = @_;

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

        # Prepare args
        my $args = {
            user => $sessionInfo->{ $self->conf->{whatToTrace} },
            ( $code ? ( code => $code ) : () ),
        };
        foreach my $k ( keys %{ $self->{initAttrs} } ) {
            $args->{$k} = $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');
    }

    return 1;
}

sub verify_external {
    my ( $self, $req, $session, $code ) = @_;

    # Prepare args
    my $args = {
        user => $session->{ $self->conf->{whatToTrace} },
        code => $code,
    };

    foreach my $k ( keys %{ $self->{vrfyAttrs} } ) {

        # in older versions, code was not automatically defined
        # if admins defined it explicitely, do not treat it as a session
        # attribute
        $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;
