<?php

/**
 * Shows the page with the FAQ record and - when available - the user comments.
 *
 * 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 https://mozilla.org/MPL/2.0/.
 *
 * @package   phpMyFAQ
 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
 * @author    Lars Tiedemann <larstiedemann@yahoo.de>
 * @copyright 2002-2026 phpMyFAQ Team
 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
 * @link      https://www.phpmyfaq.de
 * @since     2002-08-27
 */

use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\GithubFlavoredMarkdownExtension;
use League\CommonMark\MarkdownConverter;
use phpMyFAQ\Attachment\AttachmentException;
use phpMyFAQ\Attachment\AttachmentFactory;
use phpMyFAQ\Captcha\Helper\CaptchaHelper;
use phpMyFAQ\Comments;
use phpMyFAQ\Date;
use phpMyFAQ\Entity\CommentType;
use phpMyFAQ\Entity\SeoEntity;
use phpMyFAQ\Enums\PermissionType;
use phpMyFAQ\Enums\SeoType;
use phpMyFAQ\Faq\Permission;
use phpMyFAQ\Filter;
use phpMyFAQ\Glossary;
use phpMyFAQ\Helper\AttachmentHelper;
use phpMyFAQ\Helper\CommentHelper;
use phpMyFAQ\Helper\FaqHelper as HelperFaq;
use phpMyFAQ\Helper\SearchHelper;
use phpMyFAQ\Link;
use phpMyFAQ\Rating;
use phpMyFAQ\Relation;
use phpMyFAQ\Search\SearchResultSet;
use phpMyFAQ\Seo;
use phpMyFAQ\Services;
use phpMyFAQ\Session\Token;
use phpMyFAQ\Strings;
use phpMyFAQ\Tags;
use phpMyFAQ\Translation;
use phpMyFAQ\Twig\TwigWrapper;
use phpMyFAQ\User\CurrentUser;
use phpMyFAQ\Utils;
use phpMyFAQ\Visits;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

if (!defined('IS_VALID_PHPMYFAQ')) {
    http_response_code(400);
    exit();
}

$faqConfig = $container->get('phpmyfaq.configuration');
$user = $container->get('phpmyfaq.user.current_user');

$glossary = new Glossary($faqConfig);
$tagging = new Tags($faqConfig);
$tagging->setUser($currentUser)->setGroups($currentGroups);
$relation = new Relation($faqConfig);
$rating = new Rating($faqConfig);
$comment = new Comments($faqConfig);
$faqHelper = new HelperFaq($faqConfig);
$faqPermission = new Permission($faqConfig);
$seo = new Seo($faqConfig);
$attachmentHelper = new AttachmentHelper();

$faqSession = $container->get('phpmyfaq.user.session');
$faqSession->setCurrentUser($user);

$config = [
    'html_input' => 'strip',
    'allow_unsafe_links' => false,
];

$environment = new Environment($config);
$environment->addExtension(new CommonMarkCoreExtension());
$environment->addExtension(new GithubFlavoredMarkdownExtension());

$converter = new MarkdownConverter($environment);

if (is_null($user)) {
    $user = new CurrentUser($faqConfig);
}

$templateVars = [
    ...$templateVars,
];

$faqSearchResult = new SearchResultSet($user, $faqPermission, $faqConfig);

$captcha = $container->get('phpmyfaq.captcha');

$currentCategory = $cat;

$request = Request::createFromGlobals();
$faqId = Filter::filterVar($request->query->get('id'), FILTER_VALIDATE_INT, 0);
$solutionId = Filter::filterVar($request->query->get('solution_id'), FILTER_VALIDATE_INT);
$highlight = Filter::filterVar($request->query->get('highlight'), FILTER_SANITIZE_SPECIAL_CHARS);
$bookmarkAction = Filter::filterVar($request->query->get('bookmark_action'), FILTER_SANITIZE_SPECIAL_CHARS);

// Handle bookmarks
$bookmark = $container->get('phpmyfaq.bookmark');
if ($bookmarkAction === 'add' && isset($faqId)) {
    $bookmark->add($faqId);
}

if ($bookmarkAction === 'remove' && isset($faqId)) {
    $bookmark->remove($faqId);
}

// Get all data from the FAQ record
if (0 === (int) $solutionId) {
    $faq->getFaq($faqId);
} else {
    $faq->getFaqBySolutionId($solutionId);
}

if (isset($faq->faqRecord['id'])) {
    $faqId = $faq->faqRecord['id'];
}

$faqSession->userTracking('article_view', $faqId);

$faqVisits = new Visits($faqConfig);
$faqVisits->logViews((int) $faqId);

$question = $faq->getQuestion($faqId);
if ($faqConfig->get('main.enableMarkdownEditor')) {
    $answer = $converter->convert($faq->faqRecord['content'])->getContent();
} else {
    $answer = $faq->faqRecord['content'];
}

// Cleanup answer content first
$answer = $faqHelper->cleanUpContent($answer);

// Rewrite URL fragments
$currentUrl = sprintf('//%s%s', $request->getHost(), $request->getRequestUri());
$answer = $faqHelper->rewriteUrlFragments($answer, $currentUrl);

// Rewrite old internal links
$answer = $faqHelper->convertOldInternalLinks($question, $answer);

// Add Glossary entries for answers only
$answer = $glossary->insertItemsIntoContent($answer);

// Set the path of the current category
$categoryName = $category->getPath($currentCategory, ' &raquo; ', true);

if (
    !is_null($highlight)
    && $highlight != '/'
    && $highlight != '<'
    && $highlight != '>'
    && Strings::strlen($highlight) > 3
) {
    $highlight = str_replace("'", '´', (string) $highlight);
    $highlight = str_replace(['^', '.', '?', '*', '+', '{', '}', '(', ')', '[', ']'], '', $highlight);
    $highlight = preg_quote($highlight, '/');
    $searchItems = explode(' ', $highlight);

    foreach ($searchItems as $searchItem) {
        if (Strings::strlen($searchItem) > 2) {
            $question = Utils::setHighlightedString($question, $searchItem);
            $answer = Utils::setHighlightedString($answer, $searchItem);
        }
    }
}

// List all faq attachments
$attachmentList = [];
if ($faqConfig->get('records.disableAttachments') && 'yes' == $faq->faqRecord['active']) {
    try {
        $attList = AttachmentFactory::fetchByRecordId($faqConfig, $faqId);
        $attachmentList = $attachmentHelper->getAttachmentList($attList);
    } catch (AttachmentException) {
        // handle exception
    }
}

// List all categories for this faq
$renderedCategoryPath = '';
$multiCategories = $category->getCategoriesFromFaq($faqId);
if ((is_countable($multiCategories) ? count($multiCategories) : 0) >= 1) {
    foreach ($multiCategories as $multiCategory) {
        $path = $category->getPath((int) $multiCategory['id'], ' &raquo; ', true, 'list-unstyled');
        if ('' === trim($path)) {
            continue;
        }
        $renderedCategoryPath .= $path;
    }
}

// Related FAQs
try {
    $faqSearchResult->reviewResultSet($relation->getAllRelatedByQuestion(
        $faq->faqRecord['title'],
        $faq->faqRecord['keywords'],
    ));
} catch (Exception) {
    // handle exception
}

$searchHelper = new SearchHelper($faqConfig);
$relatedFaqs = $searchHelper->renderRelatedFaqs($faqSearchResult, $faqId);

// Is the faq expired?
$expired = date(format: 'YmdHis') > $faq->faqRecord['dateEnd'];

// Number of comments
$numComments = $comment->getNumberOfComments();
$comments = $comment->getCommentsData((int) $faqId, CommentType::FAQ);

$commentHelper = new CommentHelper();
$commentHelper->setConfiguration($faqConfig);

// Does the user have the right to add a comment?
if (
    -1 === $user->getUserId() && !$faqConfig->get('records.allowCommentsForGuests')
    || $faq->faqRecord['active'] === 'no'
    || 'n' === $faq->faqRecord['comment']
    || $expired
) {
    $commentMessage = Translation::get(key: 'msgWriteNoComment');
} else {
    $commentMessage = sprintf(
        '%s<a href="#" data-bs-toggle="modal" data-bs-target="#pmf-modal-add-comment">%s</a>',
        Translation::get(key: 'msgYouCan'),
        Translation::get(key: 'msgWriteComment'),
    );
    $templateVars = [
        ...$templateVars,
        'numberOfComments' => sprintf('%d %s', $numComments[$faqId] ?? 0, Translation::get(key: 'msgComments')),
        'writeCommentMsg' => $commentMessage,
    ];
}

// Display ability for saving bookmarks only for registered users
if (-1 !== $user->getUserId()) {
    $templateVars = [
        ...$templateVars,
        'bookmarkIcon' => $bookmark->isFaqBookmark($faqId) ? 'bi bi-bookmark-fill' : 'bi bi-bookmark',
        'msgAddBookmark' => $bookmark->isFaqBookmark($faqId)
            ? Translation::get(key: 'removeBookmark')
            : Translation::get(key: 'msgAddBookmark'),
        'isFaqBookmark' => $bookmark->isFaqBookmark($faqId),
    ];
}

$availableLanguages = $faqConfig->getLanguage()->isLanguageAvailable($faq->faqRecord['id']);

if (!empty($availableLanguages) && (is_countable($availableLanguages) ? count($availableLanguages) : 0) > 1) {
    $templateVars = [
        ...$templateVars,
        'msgChangeLanguage' => Translation::get(key: 'msgLanguageSubmit'),
    ];
}

if (
    $user->perm->hasPermission($user->getUserId(), PermissionType::FAQ_EDIT->value)
    && !empty($faq->faqRecord['notes'])
) {
    $templateVars = [
        ...$templateVars,
        'notesHeader' => Translation::get(key: 'ad_admin_notes'),
        'notes' => $faq->faqRecord['notes'],
    ];
}

if ('-' !== $tagging->getAllLinkTagsById($faqId)) {
    $templateVars = [
        ...$templateVars,
        'renderTagsHeader' => Translation::get(key: 'msg_tags'),
        'renderTags' => $tagging->getAllLinkTagsById($faqId),
    ];
}

if ('' !== $renderedCategoryPath) {
    $templateVars = [
        ...$templateVars,
        'renderRelatedCategoriesHeader' => Translation::get(key: 'msgArticleCategories'),
        'renderRelatedCategories' => $renderedCategoryPath,
    ];
}

if ('' !== $relatedFaqs) {
    $templateVars = [
        ...$templateVars,
        'renderRelatedArticlesHeader' => Translation::get(key: 'msg_related_articles'),
        'renderRelatedArticles' => $relatedFaqs,
    ];
}

$date = new Date($faqConfig);
$captchaHelper = CaptchaHelper::getInstance($faqConfig);

// We need some Links from social networks
$faqServices = new Services($faqConfig);
$faqServices->setCategoryId($cat);
$faqServices->setFaqId($faqId);
$faqServices->setLanguage($lang);
$faqServices->setQuestion($faq->getQuestion($faqId));

// Check if category ID and FAQ ID are linked together
if (!$category->categoryHasLinkToFaq($faqId, $currentCategory)) {
    $response = new Response();
    $response->setStatusCode(Response::HTTP_NOT_FOUND);
}

// Check if the author name should be visible, according to the GDPR option
$author = $user->getUserVisibilityByEmail($faq->faqRecord['email']) ? $faq->faqRecord['author'] : 'n/a';

// SEO
$seoEntity = new SeoEntity();
$seoEntity
    ->setSeoType(SeoType::FAQ)
    ->setReferenceId($faq->faqRecord['id'])
    ->setReferenceLanguage($faq->faqRecord['lang']);
$seoData = $seo->get($seoEntity);

$twig = new TwigWrapper(PMF_ROOT_DIR . '/assets/templates/');
$twigTemplate = $twig->loadTemplate('./faq.twig');

$templateVars = [
    ...$templateVars,
    'title' => sprintf('%s - %s', $seoData->getTitle() ?? $question, $faqConfig->getTitle()),
    'metaDescription' => $seoData->getDescription(),
    'solutionId' => $faq->faqRecord['solution_id'],
    'solutionIdLink' => Link::getSystemRelativeUri() . '?solution_id=' . $faq->faqRecord['solution_id'],
    'breadcrumb' => $category->getPathWithStartpage($cat, '/', true),
    'question' => $question,
    'answer' => $answer,
    'attachmentList' => $attachmentList,
    'faqDate' => $date->format($faq->faqRecord['created']),
    'faqLastChangeDate' => $date->format($faq->faqRecord['date']),
    'faqAuthor' => $author,
    'msgPdf' => Translation::get(key: 'msgPDF'),
    'msgPrintFaq' => Translation::get(key: 'msgPrintArticle'),
    'enableSendToFriend' => $faqConfig->get('main.enableSendToFriend'),
    'msgShareText' => Translation::get(key: 'msgShareText'),
    'msgShareViaWhatsapp' => Translation::get(key: 'msgShareViaWhatsapp'),
    'msgShareFAQ' => Translation::get(key: 'msgShareFAQ'),
    'linkToPdf' => $faqServices->getPdfLink(),
    'msgAverageVote' => Translation::get(key: 'msgAverageVote'),
    'renderVotingResult' => $rating->get($faqId),
    'switchLanguage' => $faqHelper->renderChangeLanguageSelector($faq, $currentCategory),
    'msgVoteBad' => Translation::get(key: 'msgVoteBad'),
    'msgVoteGood' => Translation::get(key: 'msgVoteGood'),
    'msgVoteSubmit' => Translation::get(key: 'msgVoteSubmit'),
    'msgWriteComment' => Translation::get(key: 'msgWriteComment'),
    'id' => $faqId,
    'lang' => $lang,
    'msgCommentHeader' => Translation::get(key: 'msgCommentHeader'),
    'msgNewContentName' => Translation::get(key: 'msgNewContentName'),
    'msgNewContentMail' => Translation::get(key: 'msgNewContentMail'),
    'defaultContentMail' => $user->getUserId() > 0 ? $user->getUserData('email') : '',
    'defaultContentName' => $user->getUserId() > 0 ? $user->getUserData('display_name') : '',
    'msgYourComment' => Translation::get(key: 'msgYourComment'),
    'msgCancel' => Translation::get(key: 'ad_gen_cancel'),
    'msgNewContentSubmit' => Translation::get(key: 'msgNewContentSubmit'),
    'csrfTokenAddComment' => Token::getInstance($container->get('session'))->getTokenString('add-comment'),
    'captchaFieldset' => $captchaHelper->renderCaptcha(
        $captcha,
        'writecomment',
        Translation::get(key: 'msgCaptcha'),
        $user->isLoggedIn(),
    ),
    'renderComments' => $commentHelper->getComments($comments),
    'msg_about_faq' => Translation::get(key: 'msg_about_faq'),
    'userId' => $user->getUserId(),
    'permissionEditFaq' => $user->perm->hasPermission($user->getUserId(), PermissionType::FAQ_EDIT->value),
    'ad_entry_edit_1' => Translation::get(key: 'ad_entry_edit_1'),
    'ad_entry_edit_2' => Translation::get(key: 'ad_entry_edit_2'),
    'bookmarkAction' => $bookmarkAction ?? '',
    'msgBookmarkAdded' => Translation::get(key: 'msgBookmarkAdded'),
    'msgBookmarkRemoved' => Translation::get(key: 'msgBookmarkRemoved'),
    'csrfTokenRemoveBookmark' => Token::getInstance($container->get('session'))->getTokenString('delete-bookmark'),
    'csrfTokenAddBookmark' => Token::getInstance($container->get('session'))->getTokenString('add-bookmark'),
];

return $templateVars;
