package Lemonldap::NG::Portal::Auth::Twitter;

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

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

our $VERSION = '2.19.0';

# INITIALIZATION

has twitterRequestTokenURL => (
    is      => 'ro',
    lazy    => 1,
    default => sub {
        $_[0]->conf->{twitterRequestTokenURL}
          || 'https://api.twitter.com/oauth/request_token';
    }
);

has twitterAuthorizeURL => (
    is      => 'ro',
    lazy    => 1,
    default => sub {
        $_[0]->conf->{twitterAuthorizeURL}
          || 'https://api.twitter.com/oauth/authorize';
    }
);

has twitterAccessTokenURL => (
    is      => 'ro',
    lazy    => 1,
    default => sub {
        $_[0]->conf->{twitterAccessTokenURL}
          || 'https://api.twitter.com/oauth/access_token';
    }
);

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

        # TODO : LWP options to use a proxy for example
        my $ua = Lemonldap::NG::Common::UserAgent->new( $_[0]->{conf} );
        $ua->env_proxy();
        return $ua;
    }
);

sub init {
    my ($self) = @_;
    unless ( $self->conf->{twitterKey} and $self->conf->{twitterSecret} ) {
        $self->error('twitterKey and twitterSecret parameters are required');
        return 0;
    }
    eval {
        require Net::OAuth;
        $Net::OAuth::PROTOCOL_VERSION = &Net::OAuth::PROTOCOL_VERSION_1_0A();
    };
    if ($@) {
        $self->error("Unable to load Net::OAuth: $@");
        return 0;
    }
    return 1;
}

sub extractFormInfo {
    my ( $self, $req ) = @_;
    my $nonce = time;

    # 1. Request to authenticate
    unless ( $req->param('twitterback') ) {
        $self->logger->debug('Redirection to Twitter');

        # 1.1 Try to get token to dialog with Twitter
        my $callback_url = $self->p->portal;

        # Twitter callback parameter
        my %prm = ( twitterback => 1 );

        # Add request state parameters
        if ( $req->data->{_url} ) {
            $prm{url} = $req->data->{_url};
        }

        # Forward hidden fields
        if ( exists $req->{portalHiddenFormValues} ) {

            $self->logger->debug("Add hidden values to Twitter redirect URL");

            foreach ( keys %{ $req->{portalHiddenFormValues} } ) {
                $prm{$_} = $req->{portalHiddenFormValues}->{$_};
            }
        }
        $callback_url .=
          ( $callback_url =~ /\?/ ? '&' : '?' ) . build_urlencoded(%prm);

        my $request = Net::OAuth->request("request token")->new(
            consumer_key     => $self->conf->{twitterKey},
            consumer_secret  => $self->conf->{twitterSecret},
            request_url      => $self->twitterRequestTokenURL,
            request_method   => 'POST',
            signature_method => 'HMAC-SHA1',
            timestamp        => time,
            nonce            => $nonce,
            callback         => $callback_url,
        );

        $request->sign;

        my $request_url = $request->to_url;

        $self->logger->debug("POST $request_url to Twitter");

        my $res = $self->ua()->post($request_url);
        $self->logger->debug( "Twitter response: " . $res->as_string );

        if ( $res->is_success ) {
            my $response = Net::OAuth->response('request token')
              ->from_post_body( $res->content );

            # 1.2 Store token key and secret in cookies (available 180s)
            $req->addCookie(
                $self->p->cookie(
                    name    => '_twitSec',
                    value   => $response->token_secret,
                    max_age => 180,
                )
            );

            # 1.3 Redirect user to Twitter
            $req->urldc( $self->twitterAuthorizeURL
                  . "?oauth_token="
                  . $response->token );
            $self->logger->debug( "Redirect user to " . $req->{urldc} );
            $req->continue(1);
            $req->steps( [] );
            return PE_OK;
        }
        else {
            $self->logger->error(
                'Twitter OAuth protocol error: ' . $res->content );
            return PE_ERROR;
        }
    }

    # 2. User is back from Twitter
    my $request_token = $req->param('oauth_token');
    my $verifier      = $req->param('oauth_verifier');
    unless ( $request_token and $verifier ) {
        $self->logger->error('Twitter OAuth protocol error');
        return PE_ERROR;
    }

    $self->logger->debug(
        "Get token $request_token and verifier $verifier from Twitter");

    # 2.1 Reconnect to Twitter
    my $access = Net::OAuth->request("access token")->new(
        consumer_key     => $self->conf->{twitterKey},
        consumer_secret  => $self->conf->{twitterSecret},
        request_url      => $self->twitterAccessTokenURL,
        request_method   => 'POST',
        signature_method => 'HMAC-SHA1',
        verifier         => $verifier,
        token            => $request_token,
        token_secret     => $self->p->cookie( name => '_twitSec' ),
        timestamp        => time,
        nonce            => $nonce,
    );
    $access->sign;

    my $access_url = $access->to_url;

    $self->logger->debug("POST $access_url to Twitter");

    my $res_access = $self->ua()->post($access_url);
    $self->logger->debug( "Twitter response: " . $res_access->as_string );

    if ( $res_access->is_success ) {
        my $response = Net::OAuth->response('access token')
          ->from_post_body( $res_access->content );

        # Get user_id and screename
        $req->data->{_twitterUserId} = $response->{extra_params}->{user_id};
        $req->data->{_twitterScreenName} =
          $response->{extra_params}->{screen_name};

        $self->logger->debug( "Get user id "
              . $req->data->{_twitterUserId}
              . " and screen name "
              . $req->data->{_twitterScreenName} );
        $req->user(
            $response->{extra_params}->{ $self->conf->{'twitterUserField'} } );
        $self->logger->debug("Good Twitter authentication for $req->{user}");
    }
    else {
        $self->logger->error(
            'Twitter OAuth protocol error: ' . $res_access->content );
        return PE_ERROR;
    }

    # Force redirection to avoid displaying OAuth data
    $req->{mustRedirect} = 1;

    # Clean temporaries cookies
    $req->addCookie(
        $self->cookie(
            name    => '_twitSec',
            value   => 0,
            expires => 'Wed, 21 Oct 2015 00:00:00 GMT'
        )
    );
    return PE_OK;
}

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

    $req->{sessionInfo}->{authenticationLevel} =
      $req->data->{twitterAuthnLevel};
    $req->{sessionInfo}->{_twitterUserId} = $req->data->{_twitterUserId};
    $req->{sessionInfo}->{_twitterScreenName} =
      $req->data->{_twitterScreenName};

    return PE_OK;
}

sub authenticate {
    return PE_OK;
}

sub authLogout {
    return PE_OK;
}

sub getDisplayType {
    return "logo";
}

1;
