<?php
/**
 * Conversations model.
 *
 * @copyright 2009-2019 Vanilla Forums Inc.
 * @license GPL-2.0-only
 * @package Conversations
 * @since 2.0
 */

/**
 * Introduces common methods that child classes can use.
 */
abstract class ConversationsModel extends Gdn_Model {

    use \Vanilla\FloodControlTrait;

    /**
     * @var \Vanilla\CacheInterface Object used to store the FloodControl data.
     */
    protected $floodGate;

    /**
     * Class constructor. Defines the related database table name.
     *
     * @since 2.2
     * @access public
     */
    public function __construct($name = '') {
        parent::__construct($name);
        $this->floodGate = FloodControlHelper::configure($this, 'Conversations', $this->Name);
    }

    /**
     * Checks to see if the user is spamming. Returns TRUE if the user is spamming.
     *
     * Users cannot post more than $SpamCount comments within $SpamTime
     * seconds or their account will be locked for $SpamLock seconds.
     *
     * @deprecated
     *
     * @since 2.2
     * @return bool Whether spam check is positive (TRUE = spammer).
     */
    public function checkForSpam($type, $skipSpamCheck = false) {
        deprecated(__CLASS__.' '.__METHOD__, 'FloodControlTrait::checkUserSpamming()');

        if ($skipSpamCheck) {
            return false;
        }

        $session = Gdn::session();

        // Validate $type
        if (!in_array($type, ['Conversation', 'ConversationMessage'])) {
            trigger_error(errorMessage(sprintf('Spam check type unknown: %s', $type), $this->Name, 'checkForSpam'), E_USER_ERROR);
        }

        $storageObject = FloodControlHelper::configure($this, 'Conversations', $type);
        return $this->checkUserSpamming($session->User->UserID, $storageObject);
    }

    /**
     * Get all the members (deleted or no) of a conversation from the $conversationID.
     *
     * @param int|array $conversationID The conversation ID or a where clause for GDN_UserConversation.
     * @param bool $idsOnly The returns only the userIDs or everything from UserConversation.
     * @param bool $limit
     * @param bool $offset
     * @param bool|null $active **true** for active participants, **false** for users who have left the conversation and **null** for everyone.
     *
     * @return array Array of users or userIDs depending on $idsOnly's value.
     */
    public function getConversationMembers($conversationID, $idsOnly = true, $limit = false, $offset = false, $active = null) {
        $conversationMembers = [];

        $userConversation = new Gdn_Model('UserConversation');
        if (is_array($conversationID)) {
            $where = $conversationID;
        } else {
            $where = ['ConversationID' => $conversationID];
        }
        if ($active === true) {
            $where['Deleted'] = 0;
        } elseif ($active === false) {
            $where['Deleted'] = 1;
        }
        $userMembers = $userConversation->getWhere($where, 'UserID', 'asc', $limit, $offset)->resultArray();

        if (is_array($userMembers) && count($userMembers)) {
            if ($idsOnly) {
                $conversationMembers = array_column($userMembers, 'UserID');
            } else {
                $conversationMembers = Gdn_DataSet::index($userMembers, 'UserID');
            }
        }

        return $conversationMembers;
    }

    /**
     * Get the count of all the members of a conversation from the $conversationID.
     *
     * @param int $conversationID The conversation ID.
     * @param bool|null $active **true** for active participants, **false** for users who have left the conversation and **null** for everyone.
     *
     * @return array Array of users or userIDs depending on $idsOnly's value.
     */
    public function getConversationMembersCount($conversationID, $active = null) {
        $userConversation = new Gdn_Model('UserConversation');

        $where = ['ConversationID' => $conversationID];

        if ($active === true) {
            $where['Deleted'] = 0;
        } elseif ($active === false) {
            $where['Deleted'] = 1;
        }

        return $userConversation->getCount($where);
    }

    /**
     * Check if user posting to the conversation is already a member.
     *
     * @param int $conversationID The conversation ID.
     * @param int $userID The user id.
     *
     * @return bool
     */
    public function validConversationMember($conversationID, $userID) {
        $conversationMembers = $this->getConversationMembers($conversationID, true, false, false, true);
        return (in_array($userID, $conversationMembers));
    }

    /**
     * Notify users when a new message is created.
     *
     * @param array|object $conversation
     * @param array|object $message
     * @param array $notifyUserIDs
     * @param array $options
     */
    protected function notifyUsers($conversation, $message, $notifyUserIDs, $options = []) {
        $conversation = (array)$conversation;
        $message = (array)$message;

        $activity = [
            'ActivityType' => 'ConversationMessage',
            'ActivityUserID' => $message['InsertUserID'],
            'HeadlineFormat' => t('HeadlineFormat.ConversationMessage', '{ActivityUserID,User} sent you a <a href="{Url,html}">message</a>'),
            'RecordType' => 'Conversation',
            'RecordID' => $conversation['ConversationID'],
            'Story' => $message['Body'],
            'ActionText' => $options['ActionText'] ?? t('Reply'),
            'Format' => val('Format', $message, c('Garden.InputFormatter')),
            'Route' => $options['Url'] ?? "/messages/{$conversation['ConversationID']}#Message_{$message['MessageID']}"
        ];

        $subject = $conversation['Subject'] ?? '';
        if ($subject) {
            if (empty($options['FirstMessage'])) {
                $subject = sprintf(t('Re: %s'), $subject);
            }
            $options['EmailSubject'] = $subject;
        } else {
            $options = [];
        }

        $activityModel = new ActivityModel();
        foreach ($notifyUserIDs as $userID) {
            if ($message['InsertUserID'] == $userID) {
                continue; // Don't notify self.
            }

            $activity['NotifyUserID'] = $userID;
            $activityModel->queue($activity, 'ConversationMessage', $options);
        }
        $activityModel->saveQueue();
    }
}
