<?php

/**
 * The main Comment class.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public License,
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
 * obtain one at http://mozilla.org/MPL/2.0/.
 *
 * @package   phpMyFAQ
 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
 * @copyright 2006-2020 phpMyFAQ Team
 * @license   http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
 * @link      https://www.phpmyfaq.de
 * @since     2006-07-23
 */

namespace phpMyFAQ;

use phpMyFAQ\Entity\CommentType;
use phpMyFAQ\Services\Gravatar;
use phpMyFAQ\Entity\Comment;

/**
 * Class Comments
 * @package phpMyFAQ
 */
class Comments
{
    /**
     * @var Configuration
     */
    private $config;

    /**
     * Language strings.
     *
     * @var string
     */
    private $pmfStr;

    /**
     * Constructor.
     *
     * @param Configuration $config
     */
    public function __construct(Configuration $config)
    {
        global $PMF_LANG;

        $this->config = $config;
        $this->pmfStr = $PMF_LANG;
    }

    /**
     * Returns all user comments (HTML formatted) from a record by type.
     *
     * @param int $id Comment ID
     * @param string $type Comment type: {faq|news}
     * @return string
     * @throws \Exception
     * @todo   Move this code to a helper class
     */
    public function getComments(int $id, string $type): string
    {
        $comments = $this->getCommentsData($id, $type);
        $date = new Date($this->config);
        $mail = new Mail($this->config);
        $gravatar = new Gravatar();

        $output = '';
        foreach ($comments as $item) {
            $output .= '<div class="row mt-2 mb-2">';
            $output .= '  <div class="col-sm-1">';
            $output .= '    <div class="thumbnail">';
            $output .= $gravatar->getImage($item->getEmail(), ['class' => 'img-thumbnail']);
            $output .= '   </div>';
            $output .= '  </div>';

            $output .= '  <div class="col-sm-11">';
            $output .= '    <div class="card">';
            $output .= '     <div class="card-header card-header-comments">';
            $output .= sprintf(
                '<strong><a href="mailto:%s">%s</a></strong>',
                $mail->safeEmail($item->getEmail()),
                $item->getUsername()
            );
            $output .= sprintf(' <span class="text-muted">(%s)</span>', $date->format($item->getDate()));
            $output .= '     </div>';
            $output .= sprintf(
                '<div class="card-body">%s</div>',
                $this->showShortComment($item->getId(), $item->getComment())
            );
            $output .= '   </div>';
            $output .= '  </div>';
            $output .= '</div>';
        }

        return $output;
    }

    /**
     * Returns all user comments from a record by type.
     *
     * @param int $referenceId record id
     * @param string $type record type: {faq|news}
     *
     * @return Comment[]
     */
    public function getCommentsData(int $referenceId, string $type): array
    {
        $comments = [];

        $query = sprintf(
            "
            SELECT
                id_comment, id, usr, email, comment, datum
            FROM
                %sfaqcomments
            WHERE
                type = '%s'
            AND 
                id = %d",
            Database::getTablePrefix(),
            $type,
            $referenceId
        );

        $result = $this->config->getDb()->query($query);
        if ($this->config->getDb()->numRows($result) > 0) {
            while ($row = $this->config->getDb()->fetchObject($result)) {
                $comment = new Comment();
                $comment
                    ->setId($row->id_comment)
                    ->setRecordId($row->id)
                    ->setComment($row->comment)
                    ->setDate(Date::createIsoDate($row->datum, DATE_ISO8601, false))
                    ->setUsername($row->usr)
                    ->setEmail($row->email)
                    ->setType($type);
                $comments[] = $comment;
            }
        }

        return $comments;
    }

    /**
     * Adds some fancy HTML if a comment is too long.
     *
     * @param int $id
     * @param string $comment
     * @return string
     */
    private function showShortComment(int $id, string $comment): string
    {
        $words = explode(' ', nl2br($comment));
        $numWords = 0;

        $comment = '';
        foreach ($words as $word) {
            $comment .= $word . ' ';
            if (15 === $numWords) {
                $comment .= '<span class="comment-dots-' . $id . '">&hellip; </span>' .
                    '<a href="#" data-comment-id="' . $id . '" class="pmf-comments-show-more comment-show-more-' . $id .
                    '">' . $this->pmfStr['msgShowMore'] . '</a>' .
                    '<span class="comment-more-' . $id . ' d-none">';
            }
            ++$numWords;
        }

        // Convert URLs to HTML anchors
        return Utils::parseUrl($comment) . '</span>';
    }

    /**
     * Adds a new comment.
     * @param Comment $comment
     * @return bool
     */
    public function addComment(Comment $comment): bool
    {
        $query = sprintf(
            "
            INSERT INTO
                %sfaqcomments
            VALUES
                (%d, %d, '%s', '%s', '%s', '%s', %d, '%s')",
            Database::getTablePrefix(),
            $this->config->getDb()->nextId(Database::getTablePrefix() . 'faqcomments', 'id_comment'),
            $comment->getRecordId(),
            $comment->getType(),
            $comment->getUsername(),
            $this->config->getDb()->escape($comment->getEmail()),
            $this->config->getDb()->escape($comment->getComment()),
            $comment->getDate(),
            $comment->hasHelped()
        );

        if (!$this->config->getDb()->query($query)) {
            return false;
        }

        return true;
    }

    /**
     * Deletes a comment.
     *
     * @param int $recordId Record id
     * @param int $commentId Comment id
     *
     * @return bool
     */
    public function deleteComment(int $recordId, int $commentId): bool
    {
        if (!is_int($recordId) && !is_int($commentId)) {
            return false;
        }

        $query = sprintf(
            '
            DELETE FROM
                %sfaqcomments
            WHERE
                id = %d
            AND
                id_comment = %d',
            Database::getTablePrefix(),
            $recordId,
            $commentId
        );

        if (!$this->config->getDb()->query($query)) {
            return false;
        }

        return true;
    }

    /**
     * Returns the number of comments of each FAQ record as an array.
     *
     * @param string $type Type of comment: faq or news
     *
     * @return array
     */
    public function getNumberOfComments($type = CommentType::FAQ)
    {
        $num = [];

        $query = sprintf(
            "
            SELECT
                COUNT(id) AS anz,
                id
            FROM
                %sfaqcomments
            WHERE
                type = '%s'
            GROUP BY id
            ORDER BY id",
            Database::getTablePrefix(),
            $type
        );

        $result = $this->config->getDb()->query($query);
        if ($this->config->getDb()->numRows($result) > 0) {
            while ($row = $this->config->getDb()->fetchObject($result)) {
                $num[$row->id] = $row->anz;
            }
        }

        return $num;
    }

    /**
     * Returns all comments with their categories.
     *
     * @param string $type Type of comment: faq or news
     *
     * @return Comment[]
     */
    public function getAllComments($type = CommentType::FAQ)
    {
        $comments = [];

        $query = sprintf(
            "
            SELECT
                fc.id_comment AS comment_id,
                fc.id AS record_id,
                %s
                fc.usr AS username,
                fc.email AS email,
                fc.comment AS comment,
                fc.datum AS comment_date
            FROM
                %sfaqcomments fc
            %s
            WHERE
                type = '%s'",
            ($type == CommentType::FAQ) ? "fcg.category_id,\n" : '',
            Database::getTablePrefix(),
            ($type == CommentType::FAQ) ? 'LEFT JOIN
                ' . Database::getTablePrefix() . "faqcategoryrelations fcg
            ON
                fc.id = fcg.record_id\n" : '',
            $type
        );

        $result = $this->config->getDb()->query($query);
        if ($this->config->getDb()->numRows($result) > 0) {
            while ($row = $this->config->getDb()->fetchObject($result)) {
                $comment = new Comment();
                $comment
                    ->setId($row->comment_id)
                    ->setRecordId($row->record_id)
                    ->setType($type)
                    ->setComment($row->comment)
                    ->setDate($row->comment_date)
                    ->setUsername($row->username)
                    ->setEmail($row->email);

                if (isset($row->category_id)) {
                    $comment->setCategoryId($row->category_id);
                }

                $comments[] = $comment;
            }
        }

        return $comments;
    }
}
