<?php
namespace Misuzu\Changelog;

use ErrorException;
use RuntimeException;
use Index\Routing\IRouter;
use Misuzu\Pagination;
use Misuzu\Template;
use Misuzu\Auth\AuthInfo;
use Misuzu\Comments\Comments;
use Misuzu\Comments\CommentsEx;
use Misuzu\Config\IConfig;
use Misuzu\Feeds\Feed;
use Misuzu\Feeds\FeedItem;
use Misuzu\Feeds\AtomFeedSerializer;
use Misuzu\Feeds\RssFeedSerializer;
use Misuzu\Users\Users;

final class ChangelogRoutes {
    private IConfig $config;
    private Changelog $changelog;
    private Users $users;
    private AuthInfo $authInfo;
    private Comments $comments;

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

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

        $router->get('/changelog', $this->getIndex(...));
        $router->get('/changelog.rss', $this->getFeedRSS(...));
        $router->get('/changelog.atom', $this->getFeedAtom(...));
        $router->get('/changelog/change/:id', $this->getChange(...));

        $router->get('/changelog.php', function($response, $request) {
            $changeId = $request->getParam('c', FILTER_SANITIZE_NUMBER_INT);
            if($changeId) {
                $response->redirect(url('changelog-change', ['change' => $changeId]), true);
                return;
            }

            $response->redirect(url('changelog-index', [
                'date' => $request->getParam('d'),
                'user' => $request->getParam('u', FILTER_SANITIZE_NUMBER_INT),
            ]), true);
        });
    }

    private function getCommentsInfo(string $categoryName): object {
        $comments = new CommentsEx($this->authInfo, $this->comments, $this->users, $this->userInfos, $this->userColours);
        return $comments->getCommentsForLayout($categoryName);
    }

    public function getIndex($response, $request) {
        $filterDate = (string)$request->getParam('date');
        $filterUser = (string)$request->getParam('user', FILTER_SANITIZE_NUMBER_INT);
        $filterTags = (string)$request->getParam('tags');

        if(empty($filterDate))
            $filterDate = null;
        else
            try {
                $dateParts = explode('-', $filterDate, 3);
                $filterDate = gmmktime(12, 0, 0, (int)$dateParts[1], (int)$dateParts[2], (int)$dateParts[0]);
            } catch(ErrorException $ex) {
                return 404;
            }

        if(empty($filterUser))
            $filterUser = null;
        else
            try {
                $filterUser = $this->users->getUser($filterUser, 'id');
            } catch(RuntimeException $ex) {
                return 404;
            }

        if(empty($filterTags))
            $filterTags = null;
        else {
            $filterTags = explode(',', $filterTags);
            foreach($filterTags as &$tag)
                $tag = trim($tag);
        }

        $count = $this->changelog->countChanges($filterUser, $filterDate, $filterTags);
        $pagination = new Pagination($count, 30);
        if(!$pagination->hasValidOffset())
            return 404;

        $changeInfos = $this->changelog->getChanges(userInfo: $filterUser, dateTime: $filterDate, tags: $filterTags, pagination: $pagination);
        if(empty($changeInfos))
            return 404;

        $changes = [];

        foreach($changeInfos as $changeInfo) {
            $userId = $changeInfo->getUserId();

            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;
            }

            $changes[] = [
                'change' => $changeInfo,
                'user' => $userInfo,
                'user_colour' => $userColour,
            ];
        }

        return Template::renderRaw('changelog.index', [
            'changelog_infos' => $changes,
            'changelog_date' => $filterDate,
            'changelog_user' => $filterUser,
            'changelog_tags' => $filterTags,
            'changelog_pagination' => $pagination,
            'comments_info' => empty($filterDate) ? null : $this->getCommentsInfo($changeInfos[0]->getCommentsCategoryName()),
        ]);
    }

    public function getChange($response, $request, string $changeId) {
        try {
            $changeInfo = $this->changelog->getChange($changeId);
        } catch(RuntimeException $ex) {
            return 404;
        }

        $tagInfos = $this->changelog->getTags(changeInfo: $changeInfo);

        try {
            $userInfo = $this->users->getUser($changeInfo->getUserId(), 'id');
            $userColour = $this->users->getUserColour($userInfo);
        } catch(RuntimeException $ex) {
            $userInfo = null;
            $userColour = null;
        }

        return Template::renderRaw('changelog.change', [
            'change_info' => $changeInfo,
            'change_tags' => $tagInfos,
            'change_user_info' => $userInfo,
            'change_user_colour' => $userColour,
            'comments_info' => $this->getCommentsInfo($changeInfo->getCommentsCategoryName()),
        ]);
    }

    private function createFeed(string $feedMode): Feed {
        $siteName = $this->config->getString('site.name', 'Misuzu');
        $changes = $this->changelog->getChanges(pagination: new Pagination(10));

        $feed = (new Feed)
            ->setTitle($siteName . ' » Changelog')
            ->setDescription('Live feed of changes to ' . $siteName . '.')
            ->setContentUrl(url_prefix(false) . url('changelog-index'))
            ->setFeedUrl(url_prefix(false) . url("changelog-feed-{$feedMode}"));

        foreach($changes as $change) {
            $changeUrl = url_prefix(false) . url('changelog-change', ['change' => $change->getId()]);
            $commentsUrl = url_prefix(false) . url('changelog-change-comments', ['change' => $change->getId()]);

            $feedItem = (new FeedItem)
                ->setTitle($change->getActionText() . ': ' . $change->getSummary())
                ->setCreationDate($change->getCreatedTime())
                ->setUniqueId($changeUrl)
                ->setContentUrl($changeUrl)
                ->setCommentsUrl($commentsUrl);

            $feed->addItem($feedItem);
        }

        return $feed;
    }

    public function getFeedRSS($response) {
        $response->setContentType('application/rss+xml; charset=utf-8');
        return (new RssFeedSerializer)->serializeFeed($this->createFeed('rss'));
    }

    public function getFeedAtom($response) {
        $response->setContentType('application/atom+xml; charset=utf-8');
        return (new AtomFeedSerializer)->serializeFeed($this->createFeed('atom'));
    }
}
