<?php
namespace Misuzu\Changelog;

use ErrorException;
use RuntimeException;
use Index\Routing\Route;
use Index\Routing\RouteHandler;
use Misuzu\Pagination;
use Misuzu\SiteInfo;
use Misuzu\Template;
use Misuzu\Auth\AuthInfo;
use Misuzu\Comments\Comments;
use Misuzu\Comments\CommentsEx;
use Misuzu\Feeds\Feed;
use Misuzu\Feeds\FeedItem;
use Misuzu\Feeds\AtomFeedSerializer;
use Misuzu\Feeds\RssFeedSerializer;
use Misuzu\URLs\URLInfo;
use Misuzu\URLs\URLRegistry;
use Misuzu\Users\UsersContext;

final class ChangelogRoutes extends RouteHandler {
    public function __construct(
        private SiteInfo $siteInfo,
        private URLRegistry $urls,
        private Changelog $changelog,
        private UsersContext $usersCtx,
        private AuthInfo $authInfo,
        private Comments $comments
    ) {}

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

    #[Route('GET', '/changelog')]
    #[URLInfo('changelog-index', '/changelog', ['date' => '<date>', 'user' => '<user>', 'tags' => '<tags>', 'p' => '<page>'])]
    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->usersCtx->getUserInfo($filterUser);
            } 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) {
            $userInfo = $changeInfo->hasUserId() ? $this->usersCtx->getUserInfo($changeInfo->getUserId()) : null;

            $changes[] = [
                'change' => $changeInfo,
                'user' => $userInfo,
                'user_colour' => $this->usersCtx->getUserColour($userInfo),
            ];
        }

        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()),
        ]);
    }

    #[Route('GET', '/changelog/change/:id')]
    #[URLInfo('changelog-change', '/changelog/change/<change>')]
    #[URLInfo('changelog-change-comments', '/changelog/change/<change>', fragment: 'comments')]
    public function getChange($response, $request, string $changeId) {
        try {
            $changeInfo = $this->changelog->getChange($changeId);
        } catch(RuntimeException $ex) {
            return 404;
        }

        $tagInfos = $this->changelog->getTags(changeInfo: $changeInfo);
        $userInfo = $changeInfo->hasUserId() ? $this->usersCtx->getUserInfo($changeInfo->getUserId()) : null;

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

    private function createFeed(string $feedMode): Feed {
        $siteName = $this->siteInfo->getName();
        $changes = $this->changelog->getChanges(pagination: new Pagination(10));

        $feed = (new Feed)
            ->setTitle($siteName . ' » Changelog')
            ->setDescription('Live feed of changes to ' . $siteName . '.')
            ->setContentUrl($this->siteInfo->getURL() . $this->urls->format('changelog-index'))
            ->setFeedUrl($this->siteInfo->getURL() . $this->urls->format("changelog-feed-{$feedMode}"));

        foreach($changes as $change) {
            $changeUrl = $this->siteInfo->getURL() . $this->urls->format('changelog-change', ['change' => $change->getId()]);
            $commentsUrl = $this->siteInfo->getURL() . $this->urls->format('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;
    }

    #[Route('GET', '/changelog.rss')]
    #[URLInfo('changelog-feed-rss', '/changelog.rss')]
    public function getFeedRSS($response) {
        $response->setContentType('application/rss+xml; charset=utf-8');
        return (new RssFeedSerializer)->serializeFeed($this->createFeed('rss'));
    }

    #[Route('GET', '/changelog.atom')]
    #[URLInfo('changelog-feed-atom', '/changelog.atom')]
    public function getFeedAtom($response) {
        $response->setContentType('application/atom+xml; charset=utf-8');
        return (new AtomFeedSerializer)->serializeFeed($this->createFeed('atom'));
    }
}
