<?php
namespace Misuzu\Home;

use RuntimeException;
use Index\DateTime;
use Index\Data\DbTools;
use Index\Data\IDbConnection;
use Index\Routing\IRouter;
use Misuzu\Pagination;
use Misuzu\Template;
use Misuzu\Auth\AuthInfo;
use Misuzu\Changelog\Changelog;
use Misuzu\Comments\Comments;
use Misuzu\Config\IConfig;
use Misuzu\Counters\Counters;
use Misuzu\News\News;
use Misuzu\Users\Users;

class HomeRoutes {
    private IConfig $config;
    private IDbConnection $dbConn;
    private AuthInfo $authInfo;
    private Changelog $changelog;
    private Comments $comments;
    private Counters $counters;
    private News $news;
    private Users $users;

    public function __construct(
        IRouter $router,
        IConfig $config,
        IDbConnection $dbConn,
        AuthInfo $authInfo,
        Changelog $changelog,
        Comments $comments,
        Counters $counters,
        News $news,
        Users $users
    ) {
        $this->config = $config;
        $this->dbConn = $dbConn;
        $this->authInfo = $authInfo;
        $this->changelog = $changelog;
        $this->comments = $comments;
        $this->counters = $counters;
        $this->news = $news;
        $this->users = $users;

        $router->get('/', [$this, 'getIndex']);

        if(MSZ_DEBUG)
            $router->get('/dev-landing', [$this, 'getLanding']);

        $router->get('/index.php', function($response) {
            $response->redirect(url('index'), true);
        });
    }

    private function getStats(): array {
        return $this->counters->get([
            'users:active',
            'users:online:recent',
            'users:online:today',
            'comments:posts:visible',
            'forum:topics:visible',
            'forum:posts:visible',
        ]);
    }

    private function getOnlineUsers(): array {
        return $this->users->getUsers(
            lastActiveInMinutes: 5,
            deleted: false,
            orderBy: 'random',
        );
    }

    private array $userInfos = [];
    private array $userColours = [];
    private array $newsCategoryInfos = [];

    private function getFeaturedNewsPosts(int $amount, bool $decorate): array {
        $postInfos = $this->news->getPosts(
            onlyFeatured: true,
            pagination: new Pagination($amount)
        );

        if(!$decorate)
            return $postInfos;

        $posts = [];

        foreach($postInfos as $postInfo) {
            $userId = $postInfo->getUserId();
            $categoryId = $postInfo->getCategoryId();

            if(array_key_exists($userId, $this->userInfos)) {
                $userInfo = $this->userInfos[$userId];
                $userColour = $this->userColours[$userId];
            } else {
                try {
                    $userInfo = $this->users->getUser($userId, 'id');
                    $userColour = $this->users->getUserColour($userInfo);
                } catch(RuntimeException $ex) {
                    $userInfo = null;
                    $userColour = null;
                }

                $this->userInfos[$userId] = $userInfo;
                $this->userColours[$userId] = $userColour;
            }

            if(array_key_exists($categoryId, $this->newsCategoryInfos))
                $categoryInfo = $this->newsCategoryInfos[$categoryId];
            else
                $this->newsCategoryInfos[$categoryId] = $categoryInfo = $this->news->getCategory(postInfo: $postInfo);

            $commentsCount = $postInfo->hasCommentsCategoryId()
                ? $this->comments->countPosts(categoryInfo: $postInfo->getCommentsCategoryId(), deleted: false) : 0;

            $posts[] = [
                'post' => $postInfo,
                'category' => $categoryInfo,
                'user' => $userInfo,
                'user_colour' => $userColour,
                'comments_count' => $commentsCount,
            ];
        }

        return $posts;
    }

    public function getPopularForumTopics(array $categoryIds): array {
        $args = 0;
        $stmt = $this->dbConn->prepare(
            'SELECT t.topic_id, c.forum_id, t.topic_title, c.forum_icon, t.topic_count_views'
            . ', (SELECT COUNT(*) FROM msz_forum_posts AS p WHERE p.topic_id = t.topic_id AND post_deleted IS NULL)'
            . ' FROM msz_forum_topics AS t'
            . ' LEFT JOIN msz_forum_categories AS c ON c.forum_id = t.forum_id'
            . ' WHERE c.forum_id IN (' . DbTools::prepareListString($categoryIds) . ') AND topic_deleted IS NULL AND topic_locked IS NULL'
            . ' ORDER BY (SELECT COUNT(*) FROM msz_forum_posts AS p WHERE p.topic_id = t.topic_id AND post_deleted IS NULL AND post_created > NOW() - INTERVAL 3 MONTH) DESC'
            . ' LIMIT 10'
        );

        foreach($categoryIds as $categoryId)
            $stmt->addParameter(++$args, (string)$categoryId);
        $stmt->execute();

        $topics = [];
        $result = $stmt->getResult();

        while($result->next())
            $topics[] = [
                'topic_id' => $result->getInteger(0),
                'forum_id' => $result->getInteger(1),
                'topic_title' => $result->getString(2),
                'forum_icon' => $result->getString(3),
                'topic_count_views' => $result->getInteger(4),
                'topic_count_posts' => $result->getInteger(5),
            ];

        return $topics;
    }

    public function getActiveForumTopics(array $categoryIds): array {
        $args = 0;
        $stmt = $this->dbConn->prepare(
            'SELECT t.topic_id, c.forum_id, t.topic_title, c.forum_icon, t.topic_count_views'
            . ', (SELECT COUNT(*) FROM msz_forum_posts AS p WHERE p.topic_id = t.topic_id AND post_deleted IS NULL)'
            . ', (SELECT MAX(post_id) FROM msz_forum_posts AS p WHERE p.topic_id = t.topic_id AND post_deleted IS NULL)'
            . ' FROM msz_forum_topics AS t'
            . ' LEFT JOIN msz_forum_categories AS c ON c.forum_id = t.forum_id'
            . ' WHERE c.forum_id IN (' . DbTools::prepareListString($categoryIds) . ') AND topic_deleted IS NULL AND topic_locked IS NULL'
            . ' ORDER BY topic_bumped DESC'
            . ' LIMIT 10'
        );

        foreach($categoryIds as $categoryId)
            $stmt->addParameter(++$args, (string)$categoryId);
        $stmt->execute();

        $topics = [];
        $result = $stmt->getResult();

        while($result->next())
            $topics[] = [
                'topic_id' => $result->getInteger(0),
                'forum_id' => $result->getInteger(1),
                'topic_title' => $result->getString(2),
                'forum_icon' => $result->getString(3),
                'topic_count_views' => $result->getInteger(4),
                'topic_count_posts' => $result->getInteger(5),
                'latest_post_id' => $result->getInteger(6),
            ];

        return $topics;
    }

    public function getIndex(...$args) {
        return $this->authInfo->isLoggedIn()
            ? $this->getHome(...$args)
            : $this->getLanding(...$args);
    }

    public function getHome() {
        $stats = $this->getStats();
        $onlineUserInfos = $this->getOnlineUsers();
        $featuredNews = $this->getFeaturedNewsPosts(5, true);
        $changelog = $this->changelog->getChanges(pagination: new Pagination(10));

        $stats['users:online:recent'] = count($onlineUserInfos);

        $birthdays = [];
        $birthdayInfos = $this->users->getUsers(deleted: false, birthdate: DateTime::now(), orderBy: 'random');
        foreach($birthdayInfos as $birthdayInfo)
            $birthdays[] = [
                'info' => $birthdayInfo,
                'colour' => $this->users->getUserColour($birthdayInfo),
            ];

        $newestMember = [];
        if(empty($birthdays)) {
            $newestMemberId = $this->config->getString('users.newest');
            if(!empty($newestMemberId))
                try {
                    $newestMemberInfo = $this->users->getUser($newestMemberId, 'id');
                    $newestMemberColour = $this->users->getUserColour($newestMemberInfo);
                    $newestMember['info'] = $newestMemberInfo;
                    $newestMember['colour'] = $newestMemberColour;
                } catch(RuntimeException $ex) {
                    $newestMember = [];
                    $this->config->removeValues('users.newest');
                }
        }

        return Template::renderRaw('home.home', [
            'statistics' => $stats,
            'newest_member' => $newestMember,
            'online_users' => $onlineUserInfos,
            'birthdays' => $birthdays,
            'featured_changelog' => $changelog,
            'featured_news' => $featuredNews,
        ]);
    }

    public function getLanding() {
        $config = $this->config->getValues([
            ['social.embed_linked:b'],
            ['landing.forum_categories:a'],
            ['site.name:s', 'Misuzu'],
            'site.url:s',
            'site.ext_logo:s',
            'social.linked:a'
        ]);

        if($config['social.embed_linked']) {
            $linkedData = [
                '@context' => 'http://schema.org',
                '@type' => 'Organization',
                'name' => $config['site.name'],
                'url' => $config['site.url'],
                'logo' => $config['site.ext_logo'],
                'same_as' => $config['social.linked'],
            ];
        } else $linkedData = null;

        $stats = $this->getStats();
        $onlineUserInfos = $this->getOnlineUsers();
        $featuredNews = $this->getFeaturedNewsPosts(3, false);
        $popularTopics = $this->getPopularForumTopics($config['landing.forum_categories']);
        $activeTopics = $this->getActiveForumTopics($config['landing.forum_categories']);

        $stats['users:online:recent'] = count($onlineUserInfos);

        return Template::renderRaw('home.landing', [
            'statistics' => $stats,
            'online_users' => $onlineUserInfos,
            'featured_news' => $featuredNews,
            'linked_data' => $linkedData,
            'forum_popular' => $popularTopics,
            'forum_active' => $activeTopics,
        ]);
    }
}
