package Lemonldap::NG::Portal::Auth::BrowserID;

use strict;
use Mouse;
use JSON;
use LWP::UserAgent;
use HTTP::Request;
use Lemonldap::NG::Portal::Main::Constants
  qw(PE_OK PE_ERROR PE_BADCREDENTIALS PE_FIRSTACCESS);

our $VERSION = '2.0.0';

extends 'Lemonldap::NG::Portal::Auth::Base';

# PROPERTIES

# return LWP::UserAgent object
has ua => (
    is      => 'rw',
    lasy    => 1,
    builder => sub {

        # TODO : LWP options to use a proxy for example
        my $ua = LWP::UserAgent->new();
        push @{ $ua->requests_redirectable }, 'POST';
        $ua->env_proxy();
        return $ua;
    }
);

# INITIALIZATION

# Enables Browser ID (required for templates) and
# LWP::UserAgent object
sub init {
    my ($self) = @_;
    $self->p->{customParameters}->{browserIdEnabled} = 1;
    foreach (
        qw(browserIdSiteName browserIdSiteLogo browserIdBackgroundColor browserIdAutoLogin)
      )
    {
        $self->p->{customParameters}->{$_} = $self->conf->{$_}
          if ( $self->conf->{$_} );
    }
    return $self->ua;
}

# RUNNING METHODS

# Get BrowserID assertion
# @return Lemonldap::NG::Portal constant
sub extractFormInfo {
    my ( $self, $req ) = @_;

    # Assertion should be browserIdAssertion parameter
    if ( my $browserIdAssertion = $req->param('browserIdAssertion') ) {
        $self->lmLog( "BrowserID Assertion found: $browserIdAssertion",
            'debug' );

        # Resolve assertion
        my $postdata =
          "assertion=$browserIdAssertion&audience=" . $self->conf->{portal};

        $self->lmLog(
            "Send $postdata to " . $self->conf->{browserIdVerificationURL},
            'debug' );

        # Prepare and launch request to BrowserID IdP
        my $request = HTTP::Request->new(
            'POST' => $self->conf->{browserIdVerificationURL} );
        $request->content_type('application/x-www-form-urlencoded');
        $request->content($postdata);

        my $answer = $self->ua()->request($request);

        $self->lmLog( "Verification response: " . $answer->as_string, 'debug' );

        # Check if HTTP response is OK
        if ( $answer->code() == "200" ) {

            # Get JSON answser and decode it
            my $browserIdAnswer = $answer->content;
            $self->lmLog( "Received BrowserID answer: $browserIdAnswer",
                'debug' );
            eval { $browserIdAnswer = JSON::from_json($browserIdAnswer); };
            if ($@) {
                $self->lmLog( "JSON decode error: $@", 'error' );
                return PE_ERROR;
            }

            # Then check for IdP response
            if ( $browserIdAnswer->{status} eq "okay" ) {
                $req->user( $browserIdAnswer->{email} );
                $self->lmLog(
                    "Found user $req->user in BrowserID verification answer",
                    'debug' );
                return PE_OK;
            }

            # Look for IdP rejection reason
            else {
                if ( $browserIdAnswer->{reason} ) {
                    $self->lmLog(
"Assertion $browserIdAssertion verification error: $browserIdAnswer->{reason}",
                        'error'
                    );

                }
                else {
                    $self->lmLog(
"Assertion $browserIdAssertion not verified by BrowserID provider",
                        'error'
                    );
                }
                return PE_BADCREDENTIALS;
            }
        }
        else {
            $self->lmLog(
"Fail to validate BrowserId assertion $browserIdAssertion: BrowserID server has returned a $answer->code code",
                'error'
            );
            return PE_ERROR;
        }
    }

    # No assertion, return to login page with BrowserID login script
    $req->{customParameters}->{browserIdLoadLoginScript} = 1;
    return PE_FIRSTACCESS;
}

sub authenticate {
    PE_OK;
}

sub setAuthSessionInfo {
    my ( $self, $req ) = @_;
    $req->{sessionInfo}->{authenticationLevel} = $self->conf->{browserIdAuthnLevel};
    $req->{sessionInfo}->{_browserIdAnswer}    = $self->conf->{browserIdAnswer};
    $req->{sessionInfo}->{_browserIdAnswerRaw} = $self->conf->{browserIdAnswerRaw};
    PE_OK;
}

sub authLogout {
    $_[1]->{customParameters}->{browserIdLoadLoginScript} = 1;
    PE_OK;
}

sub getDisplayType {
    return 'logo';
}

1;
