<?php
namespace Misuzu\Satori;

use RuntimeException;
use Index\Data\DbTools;
use Index\Data\IDbConnection;
use Index\Http\HttpFx;
use Index\Routing\IRouter;
use Misuzu\Pagination;
use Misuzu\Config\IConfig;
use Misuzu\Profile\ProfileFields;
use Misuzu\Users\Users;

final class SatoriRoutes {
    private IDbConnection $dbConn;
    private IConfig $config;
    private Users $users;
    private ProfileFields $profileFields;

    public function __construct(
        IDbConnection $dbConn,
        IConfig $config,
        IRouter $router,
        Users $users,
        ProfileFields $profileFields
    ) {
        $this->dbConn = $dbConn;
        $this->config = $config;
        $this->users = $users;
        $this->profileFields = $profileFields;

        // Simplify default error pages
        if($router instanceof HttpFx)
            $router->use('/_satori', function() use($router) {
                $router->addErrorHandler(400, function($response) {
                    $response->setContent('HTTP 400');
                });
                $router->addErrorHandler(403, function($response) {
                    $response->setContent('HTTP 403');
                });
                $router->addErrorHandler(404, function($response) {
                    $response->setContent('HTTP 404');
                });
                $router->addErrorHandler(500, function($response) {
                    $response->setContent('HTTP 500');
                });
                $router->addErrorHandler(503, function($response) {
                    $response->setContent('HTTP 503');
                });
            });

        $router->use('/_satori', [$this, 'verifyRequest']);
        $router->get('/_satori/get-profile-field', [$this, 'getProfileField']);
        $router->get('/_satori/get-recent-forum-posts', [$this, 'getRecentForumPosts']);
        $router->get('/_satori/get-recent-registrations', [$this, 'getRecentRegistrations']);
    }

    public function verifyRequest($response, $request) {
        $secretKey = $this->config->getString('secret');

        if(!empty($secretKey)) {
            $userTime = (int)$request->getHeaderLine('X-Satori-Time');
            $userHash = base64_decode((string)$request->getHeaderLine('X-Satori-Hash'));

            $currentTime = time();
            if(empty($userHash) || $userTime < $currentTime - 60 || $userTime > $currentTime + 60)
                return 403;

            $verifyText = (string)$userTime . '#' . $request->getPath() . '?' . $request->getParamString();
            $verifyHash = hash_hmac('sha256', $verifyText, $secretKey, true);

            if(!hash_equals($verifyHash, $userHash))
                return 403;
        }
    }

    public function getProfileField($response, $request): array {
        $userId = (string)$request->getParam('user', FILTER_SANITIZE_NUMBER_INT);
        $fieldId = (string)$request->getParam('field', FILTER_SANITIZE_NUMBER_INT);

        try {
            $fieldValue = $this->profileFields->getFieldValue($fieldId, $userId);
        } catch(RuntimeException $ex) {
            return ['error' => 105];
        }

        return [
            'field_value' => $fieldValue->getValue(),
        ];
    }

    public function getRecentForumPosts($response, $request): array {
        $categoryIds = $this->config->getArray('forum.categories');
        if(empty($categoryIds))
            return [];

        $batchSize = $this->config->getInteger('forum.batch', 6);
        $backlogDays = $this->config->getInteger('forum.backlog', 7);
        $startId = (string)$request->getParam('start', FILTER_SANITIZE_NUMBER_INT);

        $args = 0;
        $stmt = $this->dbConn->prepare(sprintf(
            'SELECT fp.post_id, ft.topic_id, ft.topic_title, fc.forum_id, fc.forum_name, u.user_id, u.username,'
            . ' COALESCE(u.user_colour, r.role_colour), (SELECT MIN(post_id) = fp.post_id FROM msz_forum_posts WHERE topic_id = fp.topic_id)'
            . ' FROM msz_forum_posts AS fp'
            . ' LEFT JOIN msz_users AS u ON u.user_id = fp.user_id'
            . ' LEFT JOIN msz_roles AS r ON r.role_id = u.display_role'
            . ' LEFT JOIN msz_forum_topics AS ft ON ft.topic_id = fp.topic_id'
            . ' LEFT JOIN msz_forum_categories AS fc ON fc.forum_id = fp.forum_id'
            . ' WHERE post_id > ? AND post_deleted IS NULL AND post_created >= NOW() - INTERVAL ? DAY'
            . ' AND fp.forum_id IN (%s)'
            . ' ORDER BY post_id LIMIT ?',
            DbTools::prepareListString($categoryIds)
        ));
        $stmt->addParameter(++$args, $startId);
        $stmt->addParameter(++$args, $backlogDays);
        foreach($categoryIds as $categoryId)
            $stmt->addParameter(++$args, $categoryId);
        $stmt->addParameter(++$args, $batchSize);
        $stmt->execute();

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

        while($result->next())
            $posts[] = [
                'post_id' => $result->getInteger(0),
                'topic_id' => $result->getInteger(1),
                'topic_title' => $result->getString(2),
                'forum_id' => $result->getInteger(3),
                'forum_name' => $result->getString(4),
                'user_id' => $result->getInteger(5),
                'username' => $result->getString(6),
                'user_colour' => $result->getInteger(7),
                'is_opening_post' => $result->getInteger(8),
            ];

        return $posts;
    }

    public function getRecentRegistrations($response, $request) {
        $batchSize = $this->config->getInteger('users.batch', 10);
        $backlogDays = $this->config->getInteger('users.backlog', 7);
        $startId = (string)$request->getParam('start', FILTER_SANITIZE_NUMBER_INT);

        $userInfos = $this->users->getUsers(
            after: $startId,
            newerThanDays: $backlogDays,
            orderBy: 'id',
            pagination: new Pagination($batchSize),
            deleted: false
        );

        $users = [];

        foreach($userInfos as $userInfo)
            $users[] = [
                'user_id' => (int)$userInfo->getId(),
                'username' => $userInfo->getName(),
            ];

        return $users;
    }
}
