package WebGUI::Friends;

=head1 LEGAL

 -------------------------------------------------------------------
  WebGUI is Copyright 2001-2009 Plain Black Corporation.
 -------------------------------------------------------------------
  Please read the legal notices (docs/legal.txt) and the license
  (docs/license.txt) that came with this distribution before using
  this software.
 -------------------------------------------------------------------
  http://www.plainblack.com                     info@plainblack.com
 -------------------------------------------------------------------

=cut

use strict;
use Class::InsideOut qw(id register public readonly);
use WebGUI::DateTime;
use WebGUI::HTML;
use WebGUI::Inbox;
use WebGUI::International;
use WebGUI::User;
use WebGUI::Utility;

readonly session    => my %session;
readonly user       => my %user;

=head1 NAME

WebGUI::Friends

=head1 SYNOPSIS

my $friends = WebGUI::Friends->new($session, $user);

$friends->add(\@userIds);
$friends->remove(\@userIds);

=head1 DESCRIPTION

A user relationship management system.

=head1 METHODS

=cut


#-------------------------------------------------------------------

=head2 add ( \@userIds )

Add friends. Also adds the reciprocal relationship.

=head3 userIds

An array reference of userIds to add as friends.

=cut

sub add {
    my $self = shift;
    my $userIds = shift;
    my $me = $self->user;
    $me->friends->addUsers($userIds);
    foreach my $userId (@{$userIds}) {
        my $friend = WebGUI::User->new($self->session, $userId);
        $friend->friends->addUsers([$me->userId]);
    }
}

#-------------------------------------------------------------------

=head2 approveAddRequest ( inviteId ) 

Sends an approval, sets up the relationship, and deletes the invitation.

=head3 inviteId

The unique idenitifer for this invitation.

=cut

sub approveAddRequest {
    my $self = shift;
    my $inviteId = shift;
    my $db = $self->session->db;
    my $invite = $self->getAddRequest($inviteId);
    $self->add([$invite->{inviterId}]);
    my $i18n = WebGUI::International->new($self->session, "Friends");
    my $inbox = WebGUI::Inbox->new($self->session);
    $inbox->addMessage({
        message => sprintf($i18n->get("invitation accepted by user"), $self->user->getWholeName),
        subject => $i18n->get('friends invitation accepted'),
        userId  => $invite->{inviterId},
        status  => 'unread',
        sentBy  => $self->user->userId,
    });
    my $message = $inbox->getMessage($invite->{messageId});
    if ($message) {
        $message->setStatus('completed');
    }
    $db->deleteRow("friendInvitations", "inviteId", $inviteId);
}


#-------------------------------------------------------------------

=head2 delete ( \@userIds )

Remove friends.  Also removes the reciprocal relationship.

=head3 userIds

An array reference of userIds to remove from friends list.

=cut

sub delete {
    my $self = shift;
    my $userIds = shift;
    my $me = $self->user;

    $me->friends->deleteUsers($userIds);
    foreach my $userId (@{$userIds}) {
        my $friend = WebGUI::User->new($self->session, $userId);
        $friend->friends->deleteUsers([$me->userId]);
    }
}

#-------------------------------------------------------------------

=head2 getAddRequest ( inviteId ) 

Returns the invitation data as a hash reference.

=cut

sub getAddRequest {
    my $self = shift;
    my $inviteId = shift;
    my $invite = $self->session->db->getRow('friendInvitations', 'inviteId', $inviteId);
}


#-------------------------------------------------------------------

=head2 getAllPendingAddRequests ( session )

Class method. Returns a WebGUI::SQL::ResultSet object with all the unanswered add requests.

=cut

sub getAllPendingAddRequests {
    my $class = shift;
    my $session = shift;
    return $session->db->read("select * from friendInvitations order by dateSent");
}


#-------------------------------------------------------------------

=head2 getNextInvitation ( invitation ) 

Returns the invitation that was sent to the user just after the invitation passed in.

=cut

sub getNextInvitation {
    my $self       = shift;
    my $invitation = shift;

    my $sql = q{
        select
            *
        from
            friendInvitations
        where
            friendId = ?
            and dateSent > ?
        order by dateSent asc
        limit 1
    };
    my $bindvars = [$self->user->userId,$invitation->{dateSent}];
    return $self->session->db->quickHashRef($sql,$bindvars);
}


#-------------------------------------------------------------------

=head2 getPreviousInvitation ( invitation ) 

Returns the invitation that was sent to the user just before the invitation passed in.

=cut

sub getPreviousInvitation {
    my $self       = shift;
    my $invitation = shift;

    my $sql = q{
        select
            *
        from
            friendInvitations
        where
            friendId = ?
            and dateSent < ?
        order by dateSent desc
        limit 1
    };
    my $bindvars = [$self->user->userId,$invitation->{dateSent}];
    return $self->session->db->quickHashRef($sql,$bindvars);
}

#-------------------------------------------------------------------

=head2 isFriend ( userId )

Returns a booelean indicating whether the userId is already a friend of this user.

=head3 userId

The userId to check against this user.

=cut

sub isFriend {
    my $self = shift;
    my $userId = shift;
    return isIn($userId, @{$self->user->friends->getUsers});    
}

#-------------------------------------------------------------------

=head2 isInvited ( userId )

Returns a booelean indicating whether the user has already been invited to the friends network.

=head3 userId

The userId to check against this user.

=cut

sub isInvited {
    my $self    = shift;
    my $session = $self->session;
    my $userId  = shift;
    
    my ($isInvited) = $session->db->quickArray(q{
        select
            count(*)
        from
            friendInvitations
        where
            inviterId = ?
            and friendId = ?
    },
    [$self->user->userId,$userId]);

    return $isInvited;    
}

#-------------------------------------------------------------------

=head2 new ( session, user )

Constructor.

=head3 session

A reference to the current WebGUI::Session object.

=head3 user

A reference to a WebGUI::User object that we're going to manage the friends of. Defaults to the current user
attached to the session.

=cut

sub new {
    my $class       = shift;
    my $session     = shift;
    my $user        = shift || $session->user;
    my $self        = register($class);
    $session{id $self} = $session;
    $user{id $self} = $user;
    return $self;
}

#-------------------------------------------------------------------

=head2 rejectAddRequest ( inviteId[,sendNotification] )

Sends a rejection notice, and deletes the invitation.

=head3 inviteId

The id of an invitation.

=head3 sendNotification

Boolean indicating whether or not to send out the deny notification.  Defaults to true

=cut

sub rejectAddRequest {
    my $self     = shift;
    my $inviteId = shift;
    my $notify   = shift;

    my $db = $self->session->db;
    my $invite = $self->getAddRequest($inviteId);
    my $i18n = WebGUI::International->new($self->session, "Friends");
    my $inbox = WebGUI::Inbox->new($self->session);
    
    unless (defined $notify && !$notify) {  #Notify is defined but not true
        $inbox->addMessage({
            message => sprintf($i18n->get("friends invitation not accepted by user"), $self->user->getWholeName),
            subject => $i18n->get('friends invitation not accepted'),
            userId  => $invite->{inviterId},
            status  => 'unread',
        });
    }
    my $invitation = $inbox->getMessage($invite->{messageId});
    if ($invitation) {
        $invitation->setStatus('completed');
    }
    $self->session->db->deleteRow("friendInvitations", "inviteId", $inviteId);
}

#-------------------------------------------------------------------

=head2 sendAddRequest ( userId, message, inviteUrl )

Sends a request to another user to be added to this user's friends list. Returns an invitationId.

=head3 userId

The user to invite to be a friend.

=head3 message

The message to lure them to accept.

=head3 inviteUrl

The url to view the friend request

=cut

sub sendAddRequest {
    my $self       = shift;
    my $userId     = shift;
    my $comments   = shift;
    my $url        = $self->session->url;
    my $inviteUrl  = shift || $url->append($url->getSiteURL,$url->gateway('/','op=account'));

    my $i18n = WebGUI::International->new($self->session, "Friends");

    # No sneaky attack paths...
    $comments = WebGUI::HTML::filter($comments);

    # Create the invitation url.
    my $inviteId = $self->session->id->generate();
    
    $inviteUrl = $url->append($inviteUrl,'inviteId='.$inviteId);

    # Build the message
    my $messageText = sprintf $i18n->get("invitation approval email"), $self->user->getWholeName, $self->session->url->getSiteURL.$url->gateway('/'), $comments, $inviteUrl;

    # send message
    my $message = WebGUI::Inbox->new($self->session)->addMessage({
        message => $messageText,
        subject => $i18n->get("friends network invitation"),
        userId  => $userId,
        status  => 'pending',
        sentBy  => $self->user->userId,
    });

    # Create the invitation record.
    $self->session->db->setRow(
        'friendInvitations',
        'inviteId',
        {
            inviterId   => $self->user->userId,
            friendId    => $userId,
            dateSent    => WebGUI::DateTime->new($self->session, time)->toMysql,
            comments    => $comments,
            messageId   => $message->getId,
        },
        $inviteId,
    );
    return $inviteId;
}

#-------------------------------------------------------------------

=head2 sendMessage ( subject, message, [ userIds ] )

=head3 subject

The subject of the message.

=head3 message

The message itself.

=head3 userIds

An array reference of userIds to send the message to. Defaults to all friends.

=cut

sub sendMessage {
    my $self = shift;
    my $subject = shift || "Untitled";
    my $message = shift;
    my $userIds = shift || $self->user->friends->getUsers;
    my $inbox = WebGUI::Inbox->new($self->session);
    my $myId = $self->user->userId;
    foreach my $userId (@{$userIds}) {
        $inbox->addPrivateMessage({
            message => $message,
            subject => $subject,
            userId  => $userId,
            sentBy  => $myId,
            status  => 'unread',
        });
    }
}

1;
