#------------------------------------------------------------
# Copyright (C) 2006 RoyaltyShare, Inc.   All Rights Reserved
#------------------------------------------------------------
package RSApache::Command::SessionedCommand;

#
# This class is intended to be a base class for all session based commands.
#

use strict;
use warnings;
use CGI;
use CGI::Cookie;
use URI::Escape;
use Data::Dumper;

use lib '/app/tools/appuser/lib';
use AppUser::User::User;

use lib '/app/tools/common/lib';
use Common::RSApp;
use Common::Permission;
use Common::DB::Item::ClientType;
use Common::DB::Item::Client;
use Common::Preference;
use Common::Client;

use RSApache::Message;
use RSApache::Response::RedirectLogin;

use RSApache::Command;
use base 'RSApache::Command';

use constant kAccessDeniedTemplate => '/app/tools/common/production/templates/access_denied.xsl';
use constant kMsgAccessDenied      => 'access_denied';

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

    my $response;

    # check session
    #
    $response = $self->_checkLogin();
    return $response if ($response);

    # check permissions
    #
    # Added a hack to allow portal users to change their password.
    # The login check still happens first, so this should be fairly safe.
    if ( !$self->hasPermission() && $self->cmd() ne 'changepw' ) {
        return $self->accessDeniedResponse();
    }

    # Ok, we're good to go.

    # !!! So, the command wants to seed the xml with some state.
    # !!! However, it isn't clear whether every subclass of this class
    # !!! will want the same state.  So, I'm going to make that process
    # !!! a method that we can override if necessary.
    #
    $self->_addToXML();

    # !!! Lets store our cookies
    Common::Preference::StoreCookies();

    # return nothing and let the derived Command execute
    #
    return undef;
}

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

    $self->{xml}{User}   = $self->_getUser();
    $self->{xml}{Access} = $self->{_permissions};
    $self->{xml}{Client} = Common::Client::Current();

    my @clientTypes;
    my $allClientTypes = Common::DB::Item::ClientType->GetAll();
    while ( my $clientType = $allClientTypes->next() ) {
        my %typeHash;
        my $bitMask = 1 << ( $clientType->client_type_id - 1 );
        my $isTypeFlag = $self->{xml}{Client}->isClientType($bitMask);
        $typeHash{ClientTypeID}          = $clientType->client_type_id;
        $typeHash{ClientTypeDescription} = $clientType->description;
        $typeHash{IsClientTypeFlag}      = $isTypeFlag;

        push @clientTypes, \%typeHash;
    }
    $self->{xml}{ClientTypes}{ClientType} = \@clientTypes;

    # Grab the message XML, if there is any.
    #
    $self->{xml}{Messages} = RSApache::Message::ArrayRefToMessageList( $self->getParam('msg') );

    # If there is a 'magic cookie', then echo back it's contents in the XML.
    # (This is a bit of a kludge that lets the front-end carry some state around)
    #
    my $magicCookie = $self->_getMagicCookie();
    if ( defined $magicCookie ) {
        $self->{xml}{MagicCookie} = $magicCookie;
    }

    # !!! For my next hack, I want to stash the session id in the XML for end-user timing.
    # !!! Changing this - going to return the 'request debug token' instead.
    #
    #    my $sessionID = $self->_getSessionID();
    #    if ($sessionID)
    #    {
    #        $self->{xml}{Session} = $sessionID;
    #    }
    $self->{xml}{Session} = Common::RSApp::DebugToken();
}

# _checkLogin
#
# Private method that checks to see whether the user is logged in.
#
# This will return a Response object, or undef.
# - I decided to return a Response, rather than a boolean, so that
#   we could override this method at some point to _respond_ in a
#   new and exciting way.
#
sub _checkLogin {
    my ($self) = @_;

    if ( !RSApache::RSWebApp::IsSessionValid() ) {
        return $self->invalidSessionResponse();
    }

    if ( !IsTermsValid() ) {
        return $self->invalidTermsResponse();
    }

    return undef;
}

sub invalidSessionResponse {
    my ($self) = @_;
    return RSApache::Response::RedirectLogin->new( $self->requestString() );
}

sub invalidTermsResponse {
    my ($self) = @_;
    my $redirect = uri_escape( $self->requestString() );
    return RSApache::Response::Redirect->new( "/rps/user?c=terms&redirect=" . $redirect );
}

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

    my $accessLevel = $self->_getUser()->AccessLevel();
    if ( !$self->{_permissions} ) {
        $self->{_permissions} = Common::Permission->new($accessLevel);
    }
    return $self->{_permissions}->canAccess( RSApache::RSWebApp::GetAppName(), $self->area(), $self->cmd(), $accessLevel );
}

sub accessDeniedResponse {
    my ($self) = @_;
    $self->addMessageXML( type => "error", code => kMsgAccessDenied );
    return RSApache::Response::XSLT->new( $self->{xml}, kAccessDeniedTemplate );
}

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

    if ( !$self->{_user} ) {
        my $userID = Common::RSApp::GetActiveUserID();
        $self->{_user} = AppUser::User::User->new( userID => $userID, loadSubs => 0 );
    }

    return $self->{_user};
}

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

    my %cookies     = CGI::Cookie->fetch;
    my $magicCookie = $cookies{'RS_MAGIC_COOKIE'};
    return undef unless $magicCookie;
    return $magicCookie->value();
}

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

    my %cookies = CGI::Cookie->fetch;
    my $cookie  = $cookies{'RS_USER'};
    return undef unless $cookie;
    my $value = $cookie->value();
    my @bits = my ( $sessionID, $junk ) = split( '-', $value );
    return $sessionID;
}

sub IsTermsValid {
    my $self      = shift;
    my $sessionID = RSApache::DBSession::GetSessionIDFromCookie();
    my $terms     = RPS::DB::Item::Session->CheckTerms($sessionID);

    my $cgi = new CGI;

    # Let's see if the user has agree to our latest terms.
    #
    if ($terms) {
        return 1;
    } elsif ( $cgi->param('c') eq 'terms' ) {

        # They are on the terms page, so leave them alone.
        #
        return 1;
    } elsif ( $cgi->param('c') eq 'login' ) {

        # They are trying to log in, so leave them alone.
        #
        return 1;
    } else {
        return undef;
    }
}

###
1;    # Play nicely.
###

