<?php
namespace Misuzu;

use stdClass;
use RuntimeException;

$forum = $msz->getForum();
$users = $msz->getUsers();

$postId = !empty($_GET['p']) && is_string($_GET['p']) ? (int)$_GET['p'] : 0;
$topicId = !empty($_GET['t']) && is_string($_GET['t']) ? (int)$_GET['t'] : 0;
$categoryId = null;
$moderationMode = !empty($_GET['m']) && is_string($_GET['m']) ? (string)$_GET['m'] : '';
$submissionConfirmed = !empty($_GET['confirm']) && is_string($_GET['confirm']) && $_GET['confirm'] === '1';

$currentUser = $msz->getActiveUser();
$currentUserId = $currentUser === null ? '0' : $currentUser->getId();

if($topicId < 1 && $postId > 0) {
    try {
        $postInfo = $forum->getPost(postId: $postId);
    } catch(RuntimeException $ex) {
        echo render_error(404);
        return;
    }

    $categoryId = $postInfo->getCategoryId();
    $perms = $msz->getAuthInfo()->getPerms('forum', $postInfo->getCategoryId());
    $canDeleteAny = $perms->check(Perm::F_POST_DELETE_ANY);

    if($postInfo->isDeleted() && !$canDeleteAny) {
        echo render_error(404);
        return;
    }

    $topicId = $postInfo->getTopicId();
    $preceedingPostCount = $forum->countPosts(
        topicInfo: $topicId,
        upToPostInfo: $postInfo,
        deleted: $canDeleteAny ? null : false
    );
}

try {
    $topicIsNuked = $topicIsDeleted = $canDeleteAny = false;
    $topicInfo = $forum->getTopic(topicId: $topicId);
} catch(RuntimeException $ex) {
    $topicIsNuked = true;
}

if(!$topicIsNuked) {
    $topicIsDeleted = $topicInfo->isDeleted();

    if($categoryId !== (int)$topicInfo->getCategoryId()) {
        $categoryId = (int)$topicInfo->getCategoryId();
        $perms = $msz->getAuthInfo()->getPerms('forum', $topicInfo->getCategoryId());
    }

    if($msz->hasActiveBan())
        $perms = $perms->apply(fn($calc) => $calc & (Perm::F_CATEGORY_LIST | Perm::F_CATEGORY_VIEW));

    $canDeleteAny = $perms->check(Perm::F_POST_DELETE_ANY);
}

if(($topicIsNuked || $topicIsDeleted) && $forum->hasTopicRedirect($topicId)) {
    $topicRedirectInfo = $forum->getTopicRedirect($topicId);
    Template::set('topic_redir_info', $topicRedirectInfo);

    if($topicIsNuked || !$canDeleteAny) {
        if(empty($topicRedirectInfo))
            echo render_error(404);
        else
            header('Location: ' . $topicRedirectInfo->getLinkTarget());
        return;
    }
}

if(!$perms->check(Perm::F_CATEGORY_VIEW)) {
    echo render_error(403);
    return;
}

// Maximum amount of posts a topic may contain to still be deletable by the author
// this should be in the config
$deletePostThreshold = 1;

$categoryInfo = $forum->getCategory(topicInfo: $topicInfo);
$topicIsLocked = $topicInfo->isLocked();
$topicIsArchived = $categoryInfo->isArchived();
$topicPostsTotal = $topicInfo->getTotalPostsCount();
$topicIsFrozen = $topicIsArchived || $topicIsDeleted;
$canDeleteOwn = !$topicIsFrozen && !$topicIsLocked && $perms->check(Perm::F_POST_DELETE_OWN);
$canBumpTopic = !$topicIsFrozen && $perms->check(Perm::F_TOPIC_BUMP);
$canLockTopic = !$topicIsFrozen && $perms->check(Perm::F_TOPIC_LOCK);
$canNukeOrRestore = $canDeleteAny && $topicIsDeleted;
$canDelete = !$topicIsDeleted && (
    $canDeleteAny || (
        $topicPostsTotal > 0
        && $topicPostsTotal <= $deletePostThreshold
        && $canDeleteOwn
        && $topicInfo->getUserId() === (string)$currentUserId
    )
);

$validModerationModes = [
    'delete', 'restore', 'nuke',
    'bump', 'lock', 'unlock',
];

if(in_array($moderationMode, $validModerationModes, true)) {
    if(!CSRF::validateRequest()) {
        echo render_info("Couldn't verify this request, please refresh the page and try again.", 403);
        return;
    }

    if(!$msz->isLoggedIn()) {
        echo render_info('You must be logged in to manage posts.', 401);
        return;
    }

    if($msz->hasActiveBan()) {
        echo render_info('You have been banned, check your profile for more information.', 403);
        return;
    }

    switch($moderationMode) {
        case 'delete':
            if($canDeleteAny) {
                if($topicInfo->isDeleted()) {
                    echo render_info('This topic has already been marked as deleted.', 404);
                    return;
                }
            } else {
                if($topicInfo->isDeleted()) {
                    echo render_error(404);
                    return;
                }

                if(!$canDeleteOwn) {
                    echo render_info("You aren't allowed to delete topics.", 403);
                    return;
                }

                if($topicInfo->getUserId() !== $currentUser->getId()) {
                    echo render_info('You can only delete your own topics.', 403);
                    return;
                }

                // topics may only be deleted within a day of creation, this should be a config value
                $deleteTimeFrame = 60 * 60 * 24;
                if($topicInfo->getCreatedTime() < time() - $deleteTimeFrame) {
                    echo render_info('This topic has existed for too long. Ask a moderator to remove if it absolutely necessary.', 403);
                    return;
                }

                // deleted posts are intentionally included
                $topicPostCount = $forum->countPosts(topicInfo: $topicInfo);
                if($topicPostCount > $deletePostThreshold) {
                    echo render_info('This topic already has replies, you may no longer delete it. Ask a moderator to remove if it absolutely necessary.', 403);
                    return;
                }
            }

            if(!isset($_GET['confirm'])) {
                Template::render('forum.confirm', [
                    'title' => 'Confirm topic deletion',
                    'class' => 'far fa-trash-alt',
                    'message' => sprintf('You are about to delete topic #%d. Are you sure about that?', $topicInfo->getId()),
                    'params' => [
                        't' => $topicInfo->getId(),
                        'm' => 'delete',
                    ],
                ]);
                break;
            } elseif(!$submissionConfirmed) {
                url_redirect(
                    'forum-topic',
                    ['topic' => $topicInfo->getId()]
                );
                break;
            }

            $forum->deleteTopic($topicInfo->getId());
            $msz->createAuditLog('FORUM_TOPIC_DELETE', [$topicInfo->getId()]);

            url_redirect('forum-category', [
                'forum' => $categoryInfo->getId(),
            ]);
            break;

        case 'restore':
            if(!$canNukeOrRestore) {
                echo render_error(403);
                break;
            }

            if(!isset($_GET['confirm'])) {
                Template::render('forum.confirm', [
                    'title' => 'Confirm topic restore',
                    'class' => 'fas fa-magic',
                    'message' => sprintf('You are about to restore topic #%d. Are you sure about that?', $topicInfo->getId()),
                    'params' => [
                        't' => $topicInfo->getId(),
                        'm' => 'restore',
                    ],
                ]);
                break;
            } elseif(!$submissionConfirmed) {
                url_redirect('forum-topic', [
                    'topic' => $topicInfo->getId(),
                ]);
                break;
            }

            $forum->restoreTopic($topicInfo->getId());
            $msz->createAuditLog('FORUM_TOPIC_RESTORE', [$topicInfo->getId()]);

            url_redirect('forum-category', [
                'forum' => $categoryInfo->getId(),
            ]);
            break;

        case 'nuke':
            if(!$canNukeOrRestore) {
                echo render_error(403);
                break;
            }

            if(!isset($_GET['confirm'])) {
                Template::render('forum.confirm', [
                    'title' => 'Confirm topic nuke',
                    'class' => 'fas fa-radiation',
                    'message' => sprintf('You are about to PERMANENTLY DELETE topic #%d. Are you sure about that?', $topicInfo->getId()),
                    'params' => [
                        't' => $topicInfo->getId(),
                        'm' => 'nuke',
                    ],
                ]);
                break;
            } elseif(!$submissionConfirmed) {
                url_redirect('forum-topic', [
                    'topic' => $topicInfo->getId(),
                ]);
                break;
            }

            $forum->nukeTopic($topicInfo->getId());
            $msz->createAuditLog('FORUM_TOPIC_NUKE', [$topicInfo->getId()]);

            url_redirect('forum-category', [
                'forum' => $categoryInfo->getId(),
            ]);
            break;

        case 'bump':
            if($canBumpTopic) {
                $forum->bumpTopic($topicInfo->getId());
                $msz->createAuditLog('FORUM_TOPIC_BUMP', [$topicInfo->getId()]);
            }

            url_redirect('forum-topic', [
                'topic' => $topicInfo->getId(),
            ]);
            break;

        case 'lock':
            if($canLockTopic && !$topicIsLocked) {
                $forum->lockTopic($topicInfo->getId());
                $msz->createAuditLog('FORUM_TOPIC_LOCK', [$topicInfo->getId()]);
            }

            url_redirect('forum-topic', [
                'topic' => $topicInfo->getId(),
            ]);
            break;

        case 'unlock':
            if($canLockTopic && $topicIsLocked) {
                $forum->unlockTopic($topicInfo->getId());
                $msz->createAuditLog('FORUM_TOPIC_UNLOCK', [$topicInfo->getId()]);
            }

            url_redirect('forum-topic', [
                'topic' => $topicInfo->getId(),
            ]);
            break;
    }
    return;
}

$topicPosts = $topicInfo->getPostsCount();
if($canDeleteAny)
    $topicPosts += $topicInfo->getDeletedPostsCount();

$topicPagination = new Pagination($topicPosts, 10, 'page');

if(isset($preceedingPostCount))
    $topicPagination->setPage(floor($preceedingPostCount / $topicPagination->getRange()), true);

if(!$topicPagination->hasValidOffset()) {
    echo render_error(404);
    return;
}

$postInfos = $forum->getPosts(
    topicInfo: $topicInfo,
    deleted: $perms->check(Perm::F_POST_DELETE_ANY) ? null : false,
    pagination: $topicPagination,
);

if(empty($postInfos)) {
    echo render_error(404);
    return;
}

$originalPostInfo = $forum->getPost(topicInfo: $topicInfo);

$userInfos = [];
$userColours = [];
$userPostsCounts = [];
$posts = [];

foreach($postInfos as $postInfo) {
    $posts[] = $post = new stdClass;
    $post->info = $postInfo;

    if($postInfo->hasUserId()) {
        $postUserId = $postInfo->getUserId();
        if(!array_key_exists($postUserId, $userInfos)) {
            $userInfo = $users->getUser($postUserId, 'id');
            $userInfos[$postUserId] = $userInfo;
            $userColours[$postUserId] = $users->getUserColour($userInfo);
            $userPostsCounts[$postUserId] = $forum->countPosts(userInfo: $userInfo, deleted: false);
        }

        $post->user = $userInfos[$postUserId];
        $post->colour = $userColours[$postUserId];
        $post->postsCount = $userPostsCounts[$postUserId];
    }

    $post->isOriginalPost = $originalPostInfo->getId() == $postInfo->getId();
    $post->isOriginalPoster = $originalPostInfo->hasUserId() && $postInfo->hasUserId()
        && $originalPostInfo->getUserId() === $postInfo->getUserId();
}

$canReply = !$topicIsArchived && !$topicIsLocked && !$topicIsDeleted && $perms->check(Perm::F_POST_CREATE);

if(!$forum->checkUserHasReadTopic($userInfo, $topicInfo))
    $forum->incrementTopicView($topicInfo);

$forum->updateUserReadTopic($currentUser, $topicInfo);

$perms = $perms->checkMany([
    'can_create_post' => Perm::F_POST_CREATE,
    'can_edit_post' => Perm::F_POST_EDIT_OWN,
    'can_edit_any_post' => Perm::F_POST_EDIT_ANY,
    'can_delete_post' => Perm::F_POST_DELETE_OWN,
    'can_delete_any_post' => Perm::F_POST_DELETE_ANY,
]);

Template::render('forum.topic', [
    'topic_breadcrumbs' => $forum->getCategoryAncestry($topicInfo),
    'global_accent_colour' => $forum->getCategoryColour($topicInfo),
    'topic_info' => $topicInfo,
    'category_info' => $categoryInfo,
    'topic_posts' => $posts,
    'can_reply' => $canReply,
    'topic_pagination' => $topicPagination,
    'topic_can_delete' => $canDelete,
    'topic_can_nuke_or_restore' => $canNukeOrRestore,
    'topic_can_bump' => $canBumpTopic,
    'topic_can_lock' => $canLockTopic,
    'topic_user_id' => $currentUserId,
    'topic_perms' => $perms,
]);