package AppUser::Login::Command::Login;

use strict;
use warnings;
use URI::Escape;

use lib '/app/tools/api/lib';
use API::DB::Item::PortalInvite;
use API::Session;

use lib '/app/tools/appuser/lib';
use AppUser::Login::LoginForm;
use AppUser::Login::Command::MFAAuth;
use AppUser::DB::Item::User;
use AppUser::DB::Item::UserToken;

# this is a special command, there's no check for session or permission
# so we'll just derive from the basic RSApache::Command base class and define
# our "app" while other "user" Commands will derive from AppUser::Command
#
use lib '/app/tools/common/lib';
use RSApache::Response::Redirect;
use RSApache::Response::XSLT;
use Common::RSApp;
use Common::MFA::Util;
use RSApache::RSWebApp;
use RSApache::Command;

use base 'RSApache::Command';

sub cmd  { return "login"; }
sub area { return "login"; }
sub app  { return "rps"; }

use constant kTemplate => '/app/tools/appuser/templates/login.xsl';

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

    my $form;
    my $response;
    my $redirect = uri_unescape( $self->getParam("redirect") );
    my $session  = RSApache::RSWebApp::GetSession();
    my $cmd = $self->getParam("c") || '';

    my $clientID = Common::RSApp::GetClientID();

    $self->{xml}{Portal} = 1 if ( $clientID == API::Session::PORTAL_CLIENT_ID ); # portal-login

    if ( $session->IsValid() && $clientID > 0 &&
       ( $clientID != API::Session::PORTAL_CLIENT_ID || $session->GetCookie(API::Session::CURRENT_USER_COOKIE) )
    ) {
        if ($redirect && $redirect !~ /login/) {
            $response = RSApache::Response::Redirect->new($redirect);
        } elsif ( $cmd !~ /(changepw|update)/i ) {  # stay on page if changepw or update, otherwise go to home
            if ( $clientID == API::Session::PORTAL_CLIENT_ID ) {
                # Let's make sure this user has at least been invited to the portal
                my $userID = Common::RSApp::GetActiveUserID();
                my $user = AppUser::DB::Item::User->Lookup( user_id => $userID);
                my $inviteCount = API::DB::Item::PortalInvite->GetCountByInvitee( $user->email );
                if ( $inviteCount > 0 ) {
                    $response = RSApache::Response::Redirect->new("http://portal.royaltyshare.com");
                } else {
                    $response = RSApache::Response::Redirect->new("http://login.royaltyshare.com");
                }
            } else {
                $response = RSApache::Response::Redirect->new("/");
            }
        }
    } else {
        $form = AppUser::Login::LoginForm->new( redirect => $redirect );
        if ( $self->getParam('submit') ) {

            if ( $form->assignCGIParams( 'Form', $self->getParams() ) ) {

                # If we have a token, see if it's valid and if so, log us in.
                my $token = $self->getParam("token");

                my $st    = $form->validate($token);

                if ( $st == AppUser::Login::LoginForm::kMFALoginWithToken  ) {
                    $response = $form->acceptLogin();

                } elsif ( $st == AppUser::Login::LoginForm::kMFALoginWithInvalidToken ) {

                    $form->setError( Common::FormObject::kErrTokenInvalid );
                    $self->addMessageXML( type => "error", code => Common::FormObject::kErrTokenInvalid);

                } elsif ( $st == AppUser::Login::LoginForm::kLoginValidButExpired ) {

                    # User must change their password.  We'll display an interstitial page
                    # before redirecting the user to the change password page.
                    #

                    # Silently create a session so we can bring up the change password page

                    $response = $form->acceptLogin("/rps/login?c=update");

                } elsif( $st == AppUser::Login::LoginForm::kLoginValidButLocked ) {

                    # User cannot login right now ..

                    $form->setError(Common::FormObject::kErrFormSubmit);
                    $self->addMessageXML( type => "error", code => Common::FormObject::kErrUserLockedOut );
                    $self->{xml}{UserLocked} = 1;


                } elsif( $st == AppUser::Login::LoginForm::kLoginValid ) {

                    # After entering a valid login, the user must prove their identify a 2nd
                    # time.  If they do not have a MFA option selected then we'll send them
                    # to the 'choose MFA' flow; otherwise we'll send them to the 'enter MFA' flow.
                    # The user can optionally select a 'MFA remember' option to temporarily
                    # bypass subsequent MFA verification for 30 days.
                    #

                    my $email = $form->Email();

                    # Generate a token for use with the MFA dialogs
                    #
                    my $userToken = AppUser::DB::Item::UserToken->Lookup( email => $email );
                    if ( !$userToken ) {
                        $userToken = AppUser::DB::Item::UserToken->Create( email => $email );
                    }

                    $userToken->generateToken();
                    $userToken->client_id($clientID);
                    $userToken->save;
                    my $token = $userToken->token;

                    my $userObj = AppUser::DB::Item::User->Lookup( email => $email );
                    if ( defined $userObj && $userObj->user_id ) {

                        my $newCommand;
                        my $phone;
                        my $mtype; # MFA type (user.mfa_type)

                        # If the bypass cookie has expired or doesn't exist, then user must go through
                        # the MFA verification step.
                        #
                        my $bypassMFA = AppUser::Login::Command::MFAAuth->GetMFACookie($email);

                        # If the user has been reset then it's possible for the bypass cookie to still be
                        # set if the person doing the reset was on a different browser than the user that
                        # was reset.  In this case we'll remove the cookie here and force the user to config
                        # their MFA again.
                        #
                        if ( $bypassMFA && $userObj->mfa_type == AppUser::DB::Item::User::kMFATypeNone ) {
                            # Clear the 'remember me' cookie
                            AppUser::Login::Command::MFAAuth->DeleteMFACookie( $userObj->user_id );
                            $bypassMFA = 0;
                        }

                        if ( $bypassMFA ) {
                            # Go to user's dashboard
                            #
                            $response = $form->acceptLogin("?token=$token");
                        } else {
                            # MFA authentication required
                            #
                            if ( $userObj->mfa_type == AppUser::DB::Item::User::kMFATypeNone ) {
                                $newCommand='config';
                                $mtype = AppUser::DB::Item::User::kMFATypeNone;

                            } else {
                                if ( 0 && $userObj->mfa_type == AppUser::DB::Item::User::kMFATypeEmail ) {
                                    # MFA authentication via email is currently not approved.  It's disabled
                                    # here but I'm leaving the logic here in case we ever need to use it.
                                    #
                                    $mtype = AppUser::DB::Item::User::kMFATypeEmail;
                                    Common::MFA::Util->sendMFAPassword( email => $email );
                                    $newCommand = 'auth';
                                } elsif ( $userObj->mfa_type == AppUser::DB::Item::User::kMFATypeSMS ) {
                                    $mtype = AppUser::DB::Item::User::kMFATypeSMS;
                                    $phone = $userObj->mfa_phone;
                                    if ( $phone ) {
                                        Common::MFA::Util->sendSMSCode( user_id => $userObj->user_id );
                                        $newCommand = ($userObj->mfa_verified == 0 ) ? 'auth' : 'authc';
                                    } else {
                                        # This shouldn't happen, but let's just redirect to the enter-your-number page
                                        $newCommand = ($userObj->mfa_verified == 0 ) ? 'auth' : 'authc';
                                    }
                                } else {
                                    $mtype = AppUser::DB::Item::User::kMFATypeAuthenticator;
                                    $newCommand = ($userObj->mfa_verified == 0 ) ? 'auth' : 'authc';
                                }
                            }

                            my $uri = "/rps/login?c=$newCommand&email=". uri_escape($form->Email()) . "&token=$token&mtype=$mtype";
                            $uri .= "&phone=". uri_escape($phone) if ( $phone );
                            $uri .= "&redirect=". uri_escape($redirect) if ( $redirect );

                            $response = RSApache::Response::Redirect->new($uri);
                        }
                    }

                } else {
                    $form->setError(Common::FormObject::kErrFormSubmit);
                    $self->addMessageXML( type => "error", code => Common::FormObject::kErrFormSubmit );
                }
            } else {
                $form->setError(Common::FormObject::kErrFormSubmit);
                $self->addMessageXML( type => "error", code => Common::FormObject::kErrFormSubmit );
            }
        } else {
            # if this isn't a submit, but we have an email, then there was a login failure of some sort
            # clear any user token if any
            my $email = $form->Email();
            if ( $email ) {
                my $userToken = AppUser::DB::Item::UserToken->Lookup( email => $email );
                $userToken->delete if ( $userToken );
            }
        }
    }

    if ( !$response ) {
        $self->{xml}{Form} = $form;
        if ( $cmd ) {
            if( $cmd eq 'changepw' ) { # Force change password page to appear
                $self->{xml}{ForceChange} = 1; # see appuser/templates/login.xsl
            } elsif( $cmd eq 'update' ) { # Force interstitial page to appear
                $self->{xml}{ForceUpdate} = 1; # see appuser/templates/login.xsl

                # Let's also clear the portal cookie to prevent them
                # from bypassing the password change.
                API::Session->DelCookie( API::Session::CURRENT_USER_COOKIE );
            }
        }
        $response = RSApache::Response::XSLT->new( $self->{xml}, kTemplate );
    }

    return $response;
}

1;
