package Lemonldap::NG::Portal::Lib::RESTProxy;

use strict;
use JSON;
use Mouse;
use Lemonldap::NG::Common::UserAgent;
use Lemonldap::NG::Portal::Main::Constants qw(
  URIRE
  PE_OK
  PE_ERROR
  PE_BADCREDENTIALS
);
use Lemonldap::NG::Common::FormEncode;

our $VERSION = '2.19.0';

has ua             => ( is => 'rw' );
has cookieName     => ( is => 'rw' );
has sessionService => ( is => 'rw' );

# INITIALIZATION

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

    unless ( defined $self->conf->{proxyAuthService}
        && $self->conf->{proxyAuthService} =~ URIRE )
    {
        $self->error("Bad or missing proxyAuthService parameter");
        return 0;
    }

    my $sessionService = $self->conf->{proxySessionService}
      || $self->conf->{proxyAuthService} . '/session/my';
    $sessionService =~ s#/*$##;
    unless ( $sessionService =~ URIRE ) {
        $self->error("Malformed proxySessionService parameter");
        return 0;
    }
    $self->sessionService($sessionService);
    $self->ua( Lemonldap::NG::Common::UserAgent->new( $self->conf ) );
    $self->ua->default_header( Accept => 'application/json' );
    $self->cookieName( $self->conf->{proxyCookieName}
          || $self->conf->{cookieName} );

    return 1;
}

no warnings 'once';
*authenticate = \&getUser;

sub getUser {
    my ( $self, $req ) = @_;
    return PE_OK if ( $req->data->{_proxyQueryDone} );
    $self->logger->debug(
        'Proxy push auth to ' . $self->conf->{proxyAuthService} );
    my $resp = $self->ua->post(
        $self->conf->{proxyAuthService},
        {
            user     => $req->{user},
            password => $req->data->{password},
            (
                $self->conf->{proxyAuthServiceChoiceParam}
                  && $self->conf->{proxyAuthServiceChoiceValue}
                ? ( $self->conf->{proxyAuthServiceChoiceParam} =>
                      $self->conf->{proxyAuthServiceChoiceValue} )
                : ()
            ),
            (
                $self->conf->{proxyAuthServiceImpersonation}
                  && $req->param('spoofId')
                ? ( spoofId => $req->param('spoofId') )
                : ()
            )
        }
    );
    unless ( $resp->is_success ) {
        $self->logger->error(
            'Unable to query authentication service: ' . $resp->status_line );
        return PE_ERROR;
    }
    $self->logger->debug('Proxy gets a response');
    my $res = eval { JSON::from_json( $resp->content, { allow_nonref => 1 } ) };
    if ($@) {
        $self->logger->error("Bad content: $@");
        return PE_ERROR;
    }
    unless ( $res->{result} ) {
        $self->userLogger->warn("Authentication failed for $req->{user}");
        $self->setSecurity($req);
        return PE_BADCREDENTIALS;
    }
    my $name = $self->cookieName;
    unless ( grep /\b$name=/, $resp->header('Set-Cookie') ) {
        $self->logger->error("No cookie named '$name'");
        return PE_ERROR;
    }

    $req->sessionInfo->{_proxyCookies} = join '; ',
      map { s/;.*$//r } $resp->header('Set-Cookie');
    $self->logger->debug( 'Store remote cookies in session ('
          . $req->sessionInfo->{_proxyCookies}
          . ')' );
    $req->data->{_proxyQueryDone}++;

    return PE_OK;
}

sub findUser {

    # Nothing to do here
    return PE_OK;
}

sub setSessionInfo {
    my ( $self, $req ) = @_;
    return PE_OK if ( $req->data->{_setSessionInfoDone} );
    $self->logger->debug(
        'Proxy requests sessionInfo to ' . $self->sessionService . '/global' );
    my $q = HTTP::Request->new(
        GET => $self->sessionService . '/global',
        [
            Cookie => $req->sessionInfo->{_proxyCookies},
            Accept => 'application/json'
        ]
    );
    my $resp = $self->ua->request($q);
    unless ( $resp->is_success ) {
        $self->logger->error(
            'Unable to query session service: ' . $resp->status_line );
        return PE_ERROR;
    }
    $self->logger->debug('Proxy gets a response');
    my $res = eval { JSON::from_json( $resp->content, { allow_nonref => 1 } ) };
    if ($@) {
        $self->logger->error("Bad content: $@");
        return PE_ERROR;
    }
    foreach ( keys %$res ) {
        $req->{sessionInfo}->{$_} ||= $res->{$_} unless (/^_/);
    }
    $req->data->{_setSessionInfoDone}++;

    return PE_OK;
}

sub authLogout {
    my ( $self, $req ) = @_;
    $self->logger->debug(
        'Proxy ask logout to ' . $self->conf->{proxyAuthService} );
    my $q = HTTP::Request->new(
        GET => $self->conf->{proxyAuthService} . '?logout=1',
        [
            Cookie => $req->sessionInfo->{_proxyCookies},
            Accept => 'application/json'
        ]
    );
    my $resp = $self->ua->request($q);
    unless ( $resp->is_success ) {
        $self->logger->error(
            'Unable to query authentication service: ' . $resp->status_line );
        return PE_OK;
    }
    $self->logger->debug('Proxy gets a response');
    my $res = eval { JSON::from_json( $resp->content, { allow_nonref => 1 } ) };
    if ($@) {
        $self->logger->error("Bad content: $@");
        return PE_OK;
    }
    my $user = $req->{sessionInfo}->{ $self->conf->{whatToTrace} };
    unless ( $res->{result} ) {
        $self->userLogger->warn("Internal Portal logout failed for $user")
          if $user;
        return PE_OK;
    }
    $self->userLogger->notice(
        "User $user has been disconnected from internal Portal")
      if $user;

    return PE_OK;
}

1;

