<?php
namespace App\Controller;

use App\Entity\BlogPost;
use App\Entity\Note;
use App\Form\Type\NoteType;
use DateTime;
use DateTimeZone;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Twig\Attribute\Template;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use function Symfony\Component\HttpFoundation\Response;

const MONTH_NAMES = [
    '01' => 'January',
    '02' => 'February',
    '03' => 'March',
    '04' => 'April',
    '05' => 'May',
    '06' => 'June',
    '07' => 'July',
    '08' => 'August',
    '09' => 'September',
    '10' => 'October',
    '11' => 'November',
    '12' => 'December',
];

class GuiController extends AbstractController {
    /**
     * @return array<string,string>
     */
    #[Route('/', name: 'index')]
    #[Template('/index.html.twig')]
    public function index(): array {
        return [
            'title' => 'Joe Carstairs',
            'description' => 'Joe Carstairs\' personal website',
        ];
    }
    /**
     * @return array<string,mixed>
     */
    #[Route('/notes', name: 'notes')]
    #[Template('/notes.html.twig')]
    public function notes(
        EntityManagerInterface $entityManager,
    ): array {
        $notes = $entityManager->getRepository(Note::class)->findAllOrderedBySlugDesc();
        $years = $this->getYears($notes);
        $notesByYear = $this->groupByYear($notes);
        $months = array_map(array: $years, callback: fn($year) => $this->getMonths($notesByYear[$year]));
        $monthsByYear = array_combine(keys: $years, values: $months);
        $groupByMonth = fn($notes) => $this->groupByMonth($notes);
        $notesByYearAndMonth = array_map(
            array: $notesByYear,
            callback: $groupByMonth,
        );

        return [
            'title' => 'Joe Carstairs\' notes',
            'description' => 'Joe Carstairs\' notes',
            'isFeed' => True,
            'notes' => $notesByYearAndMonth,
            'years' => $years,
            'months' => $monthsByYear,
            'monthNames' => MONTH_NAMES,
        ];
    }
    /**
     * @return array<string,mixed>
     */
    #[Route('/blog', name: 'blog_posts')]
    #[Template('/blog_posts.html.twig')]
    public function blogPosts(
        EntityManagerInterface $entityManager,
    ): array {
        $posts = $entityManager->getRepository(BlogPost::class)->findAllOrderedBySlugDesc();
        $years = $this->getYears($posts);
        $postsByYear = $this->groupByYear($posts);
        $months = array_map(array: $years, callback: fn($year) => $this->getMonths($postsByYear[$year]));
        $monthsByYear = array_combine(keys: $years, values: $months);
        $groupByMonth = fn($posts) => $this->groupByMonth($posts);
        $postsByYearAndMonth = array_map(
            array: $postsByYear,
            callback: $groupByMonth,
        );

        return [
            'title' => 'Joe Carstairs\' blog',
            'description' => 'Joe Carstairs\' blog',
            'isFeed' => True,
            'posts' => $postsByYearAndMonth,
            'years' => $years,
            'months' => $monthsByYear,
            'monthNames' => MONTH_NAMES,
        ];
    }

    /**
     * @@template T of \FeedEntry
     * @param $entries T[]
     * @return T[][]
     */
    function groupByYear(array $entries): array {
        $years = $this->getYears($entries);
        $filterByYear = fn(string $year) =>
            fn($entry) => ($entry->getPublishedDate()->format('Y') == $year);
        $entriesForYear = fn(string $year) =>
            array_filter(array: $entries, callback: $filterByYear($year));
        $entriesByYear = array_map(
            array: $years,
            callback: $entriesForYear,
        );
        return array_combine(keys: $years, values: $entriesByYear);
    }

    /**
     * @template T of \FeedEntry
     * @param $entries T[]
     * @return string[]
     */
    function getYears(array $entries): array {
        $getYear = fn($note): string => $note->getPublishedDate()->format('Y');
        $years = array_map(array: $entries, callback: $getYear);
        $years = array_unique($years);
        arsort($years);
        return $years;
    }

    /**
     * @template T of \FeedEntry
     * @param $entries T[]
     * @return T[]
     */
    function groupByMonth(array $entries): array {
        $months = $this->getMonths($entries);
        $filterByMonth = fn(string $month) =>
            fn($entry) => ($entry->getPublishedDate()->format('m') == $month);
        $entriesByMonth = array_map(
            array: $months,
            callback: fn(string $month) =>
                array_filter(array: $entries, callback: $filterByMonth($month)),
        );
        return array_combine(keys: $months, values: $entriesByMonth);
    }

    /**
     * @@template T of \FeedEntry
     * @param $entries T[]
     * @return string[]
    */
    function getMonths(array $entries): array {
        $getMonth = fn($entry) => $entry->getPublishedDate()->format('m');
        $months = array_map(array: $entries, callback: $getMonth);
        $months = array_unique($months);
        arsort($months);
        return $months;
    }

    /**
     * @template T of \FeedEntry
     * @param $entries T[]
     * @return T[]
     */
    function sortBySlug(array $entries): array {
        $getSlug = fn($entry): string => $entry->getSlug();
        $slugs = array_map(array: $entries, callback: $getSlug);
        $entriesBySlug = array_combine(keys: $slugs, values: $entries);
        krsort($entriesBySlug);
        return array_values($entriesBySlug);
    }

    #[IsGranted('ROLE_EDITOR')]
    #[Route('/notes/write')]
    #[Template('/write_note.html.twig')]
    public function writeNote(
        EntityManagerInterface $entityManager,
        Request $request,
    ): Response {
        $note = new Note();
        $form = $this->createForm(NoteType::class, $note);

        $form->handleRequest($request);
        if ($form->isSubmitted() && $form->isValid()) {
            $note = $form->getData();

            $now = new DateTime('now');
            $note->setPublishedDate($now);

            $num_existing_notes_today = $entityManager->getRepository(Note::class)->countWherePublishedOnDate($now);
            $note->getPublishedDate()->setTimezone(new DateTimeZone('Europe/London'));
            $slug = $note->getPublishedDate()->format('Y-m-d');
            if ($num_existing_notes_today > 0) {
                $slug = $slug . '-' . $num_existing_notes_today;
            }
            $note->setSlug($slug);

            $entityManager->persist($note);
            $entityManager->flush();

            return $this->redirectToRoute('note', ['slug' => $slug]);
        }

        return $this->render('/write_note.html.twig', [
            'title' => 'Write for Joe Carstairs',
            'description' => 'The authoring page for Joe Carstairs\' personal website',
            'form' => $form,
        ]);
    }

    /**
     * @return array<string,mixed>
     */
    #[Route('/notes/{slug}', name: 'note')]
    #[Template('/note.html.twig')]
    public function note(
        EntityManagerInterface $entityManager,
        string $slug,
    ): array {
        $repository = $entityManager->getRepository(Note::class);
        $note = $repository->findOneBy(['slug' => $slug]);

        return [
            'title' => 'Joe Carstairs\' notes',
            'description' => 'Joe Carstairs\' notes',
            'isFeedEntry' => True,
            'note' => $note,
            'slug' => $slug,
        ];
    }

    /**
     * @return array<string,mixed>
     */
    #[Route('/blog/{slug}', name: 'blog_post')]
    #[Template('/blog_post.html.twig')]
    public function blogPost(
        EntityManagerInterface $entityManager,
        string $slug,
    ): array {
        $repository = $entityManager->getRepository(BlogPost::class);
        $post = $repository->findOneBy(['slug' => $slug]);

        return [
            'title' => $post->getTitle(),
            'description' => $post->getDescription(),
            'isFeedEntry' => True,
            'post' => $post,
            'slug' => $slug,
        ];
    }
}
