<?php

namespace App\Controller\Admin;

use App\Entity\LoginMessage;
use App\Entity\Member;
use App\Entity\Message;
use App\Entity\UploadedImage;
use App\Form\ChangeUsernameFormType;
use App\Form\CustomDataClass\Tools\ChangeUsernameRequest;
use App\Form\CustomDataClass\Tools\FindUserRequest;
use App\Form\FeedbackFormType;
use App\Form\FindUserFormType;
use App\Form\LoginMessageType;
use App\Logger\Logger;
use App\Model\FeedbackModel;
use App\Repository\MemberRepository;
use App\Repository\MessageRepository;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use Pagerfanta\Doctrine\ORM\QueryAdapter;
use Pagerfanta\Pagerfanta;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Contracts\Translation\TranslatorInterface;

/**
 * @SuppressWarnings("PHPMD.TooManyPublicMethods")
 * @SuppressWarnings("PHPMD.CouplingBetweenObjects")
 *
 * This controller handles all tools for volunteers. As they are organized by roles the sheer number es rather high.
 */
class VolunteerToolController extends AbstractController
{
    private const string CHANGE_USERNAME = 'admin.tools.change_username';
    private const string FIND_USER = 'admin.tools.find_user';
    private const string MESSAGES_SENT = 'admin.tools.messages_sent';
    private const string REQUESTS_SENT = 'admin.tools.requests_sent';
    private const string MESSAGES_BY_MEMBER = 'admin.tools.messages_by_member';
    private const string CHECK_FEEDBACK = 'admin.tools.check_feedback';
    private const string CHECK_TOP_SPAMMER = 'admin.tools.check_spam_messages';
    private const string DAMAGE_DONE = 'admin.tools.damage_done';
    private const string AGE_BY_COUNTRY = 'admin.tools.age_by_country';
    private const string UPLOADED_IMAGES = 'admin.tools.uploaded_images';
    private const string LOGIN_MESSAGES_SHOW = 'admin.tools.login_messages.show';
    private const string LOGIN_MESSAGE_ADD = 'admin.tools.login_message.add';

    public function __construct(private readonly FeedbackModel $feedbackModel)
    {
    }

    #[Route(path: '/admin/tools', name: 'admin_volunteer_tools')]
    public function showOverview(Request $request): RedirectResponse
    {
        $subMenuItems = $this->checkPermissions($request);
        $firstSubMenuItem = reset($subMenuItems);

        return $this->redirect($firstSubMenuItem['url']);
    }

    #[Route(path: '/admin/tools/change', name: 'admin_tools_change_username')]
    public function changeUsername(
        Request $request,
        EntityManagerInterface $entityManager,
        TranslatorInterface $translator,
        Logger $logger,
    ): Response {
        // check permissions
        $subMenuItems = $this->checkPermissions($request, self::CHANGE_USERNAME);

        $form = $this->createForm(ChangeUsernameFormType::class, new ChangeUsernameRequest());
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $data = $form->getData();
            $memberRepository = $entityManager->getRepository(Member::class);
            $oldMember = $memberRepository->findOneBy(['username' => $data->oldUsername]);
            if (null !== $oldMember) {
                // check if new username is already taken
                $newMember = $memberRepository->findOneBy(['username' => $data->newUsername]);
                if (null === $newMember) {
                    $logger->write(
                        'Changed member username from ' . $data->oldUsername . ' to ' . $data->newUsername . '.',
                        'adminquery'
                    );

                    $oldMember->setUsername($data->newUsername);
                    $entityManager->persist($oldMember);
                    $entityManager->flush();
                    $flashMessage = $translator->trans('flash.admin.tools.changed', [
                        '%oldname%' => $data->oldUsername,
                        '%newname%' => $data->newUsername,
                    ]);
                    $this->addFlash('notice', $flashMessage);

                    return $this->redirectToRoute('admin_tools_change_username');
                }
                $form
                    ->get('newUsername')
                    ->addError(
                        new FormError('A member with this username already exists. Please choose a different name.')
                    )
                ;
            } else {
                $form->get('oldUsername')->addError(new FormError('No member with this username found.'));
                // check if new username is already taken
                $newMember = $memberRepository->findOneBy(['username' => $data->newUsername]);
                if (null !== $newMember) {
                    $form
                        ->get('newUsername')
                        ->addError(
                            new FormError('A member with this username already exists. Please choose a different name.')
                        )
                    ;
                }
            }
        }

        return $this->render(
            'admin/tools/change.username.html.twig',
            [
                'form' => $form->createView(),
                'submenu' => [
                    'items' => $subMenuItems,
                    'active' => self::CHANGE_USERNAME,
                ],
            ]
        );
    }

    /**
     * @throws Exception
     */
    #[Route(path: '/admin/tools/findmember', name: 'admin_tools_find_user')]
    public function findUser(Request $request, EntityManagerInterface $entityManager, Logger $logger): Response
    {
        // check permissions
        $subMenuItems = $this->checkPermissions($request, self::FIND_USER);

        $form = $this->createForm(FindUserFormType::class, new FindUserRequest());
        $form->handleRequest($request);

        $members = null;
        if ($form->isSubmitted() && $form->isValid()) {
            $data = $form->getData();

            $logger->write('Searched for members using search term: ' . $data->term . '.', 'adminquery');

            /** @var MemberRepository $memberRepository */
            $memberRepository = $entityManager->getRepository(Member::class);
            $members = $memberRepository->findByProfileInfo($data->term);
        }

        return $this->render(
            'admin/tools/find.user.html.twig',
            [
                'form' => $form->createView(),
                'members' => $members,
                'submenu' => [
                    'items' => $subMenuItems,
                    'active' => self::FIND_USER,
                ],
            ]
        );
    }

    #[Route(path: '/admin/tools/check/feedback', name: 'admin_tools_check_feedback')]
    public function showSignupFeedback(Request $request): Response
    {
        // check permissions
        $subMenuItems = $this->checkPermissions($request, self::CHECK_FEEDBACK);

        $page = $request->query->get('page', 1);
        $limit = $request->query->get('limit', 20);
        $types = $request->query->get('types') ?? [];

        $categories = $this->feedbackModel->getCategories();

        $feedbackForm = $this->createForm(FeedbackFormType::class, [
            'categories' => $categories,
        ]);
        $feedbackForm->handleRequest($request);

        if ($feedbackForm->isSubmitted() && $feedbackForm->isValid()) {
            $data = $feedbackForm->getData();
            $types = $data['types'];
        }

        $feedbacks = $this->feedbackModel->getFilteredFeedback($types, $page, $limit);

        return $this->render(
            'admin/tools/check.feedback.html.twig',
            [
                'form' => $feedbackForm->createView(),
                'feedbacks' => $feedbacks,
                'submenu' => [
                    'items' => $subMenuItems,
                    'active' => self::CHECK_FEEDBACK,
                ],
            ]
        );
    }

    /**
     * @throws Exception
     */
    #[Route(path: '/admin/tools/topspammer', name: 'admin_tools_top_spammer')]
    public function showTopSpammer(Request $request, EntityManagerInterface $entityManager): Response
    {
        // check permissions
        $subMenuItems = $this->checkPermissions($request, self::CHECK_TOP_SPAMMER);

        // Get all banned members with the number of sent messages for the last two months
        $connection = $entityManager->getConnection();

        $messagesSent = $connection->executeQuery("
            SELECT
                COUNT(*) AS 'MessagesSent',
                Username AS Username,
                members.Status AS Status,
                members.updated AS Updated
            FROM
                members,
                messages
            WHERE
                messages.IdSender = members.id
                AND (members.Status = 'Banned'
                OR members.Status = 'Rejected')
                AND DATEDIFF(NOW(), members.Updated) < 91
            GROUP BY members.id
            ORDER BY MAX(members.updated) DESC
            LIMIT 100;
        ")->fetchAllAssociative();

        return $this->render(
            'admin/tools/top.spammer.html.twig',
            [
                'messagesSent' => $messagesSent,
                'submenu' => [
                    'items' => $subMenuItems,
                    'active' => self::CHECK_TOP_SPAMMER,
                ],
            ]
        );
    }

    #[Route(path: '/admin/tools/damagedone', name: 'admin_tools_damage_done')]
    public function showDamageDone(Request $request, EntityManagerInterface $entityManager): Response
    {
        // check permissions
        $subMenuItems = $this->checkPermissions($request, self::DAMAGE_DONE);

        // Get all banned members with the number of sent messages for the last two months
        $connection = $entityManager->getConnection();

        $damageDone = $connection->executeQuery("
            SELECT
                m1.Username AS 'Receiver',
                m1.Status AS 'ReceiverStatus',
                m1.updated 'LastUpdated',
                m2.Username AS 'Sender',
                m2.Status AS 'SenderStatus'
            FROM
                members AS m1,
                members AS m2,
                messages
            WHERE
                messages.IdSender = m2.id
                AND messages.IdReceiver = m1.id
                AND m2.Status IN ('Banned' , 'Rejected')
                AND m1.Status IN ('TakenOut' , 'AskToLeave')
            ORDER BY m2.Updated DESC, m2.Username, m1.updated DESC , m1.id
            LIMIT 40
        ")->fetchAllAssociative();

        return $this->render(
            'admin/tools/damage.done.html.twig',
            [
                'damageDone' => $damageDone,
                'submenu' => [
                    'items' => $subMenuItems,
                    'active' => self::DAMAGE_DONE,
                ],
            ]
        );
    }

    #[Route(path: '/admin/tools/messages/sent', name: 'admin_tools_messages_sent')]
    public function showMessagesLastWeekAction(Request $request, EntityManagerInterface $entityManager): Response
    {
        // check permissions
        $subMenuItems = $this->checkPermissions($request, self::MESSAGES_SENT);

        $connection = $entityManager->getConnection();
        $results = $connection->executeQuery('
        SELECT
m.username AS Username,
g.name AS City,
g.country AS Country,
count(msg.id) AS Count
FROM
messages msg,
members m
LEFT JOIN geonames g ON m.IdCity = g.geonameID
WHERE
m.id = msg.IdSender
AND (DATE_ADD(msg.created,
    INTERVAL 7 DAY) > NOW())
GROUP BY m.Username
HAVING COUNT(msg.id) > 9
ORDER BY count(msg.id) DESC')->fetchAllAssociative();

        return $this->render(
            'admin/tools/messages.sent.html.twig',
            [
                'results' => $results,
                'submenu' => [
                    'items' => $subMenuItems,
                    'active' => self::MESSAGES_SENT,
                ],
            ]
        );
    }

    #[Route(path: '/admin/tools/requests/sent', name: 'admin_tools_requests_sent')]
    public function showRequestsLastTwoWeeks(Request $request, EntityManagerInterface $entityManager): Response
    {
        // check permissions
        $subMenuItems = $this->checkPermissions($request, self::REQUESTS_SENT);

        $connection = $entityManager->getConnection();
        $results = $connection->executeQuery('
        SELECT
m.username AS Username,
g.name AS City,
g.country AS Country,
count(msg.id) AS Count
FROM
messages msg,
members m
LEFT JOIN geonames g ON m.IdCity = g.geonameID
WHERE
m.id = msg.IdSender
AND NOT msg.request_id IS NULL
AND (DATE_ADD(msg.created,
    INTERVAL 14 DAY) > NOW())
GROUP BY m.Username
HAVING COUNT(msg.id) > 9
ORDER BY count(msg.id) DESC')->fetchAllAssociative();

        return $this->render(
            'admin/tools/requests.sent.html.twig',
            [
                'results' => $results,
                'submenu' => [
                    'items' => $subMenuItems,
                    'active' => self::REQUESTS_SENT,
                ],
            ]
        );
    }

    #[Route(path: '/admin/tools/messages/member', name: 'admin_tools_messages_by_member')]
    public function showMessagesByMember(Request $request, EntityManagerInterface $entityManager): Response
    {
        $subMenuItems = $this->checkPermissions($request, self::MESSAGES_BY_MEMBER);

        $usernameForm = $this->createFormBuilder()
            ->add('username', TextType::class, [
                'attr' => [
                    'class' => 'member-autocomplete',
                ],
                'constraints' => [
                    new NotBlank(),
                ],
            ])
            ->add('submit', SubmitType::class)
            ->setMethod('POST')
            ->getForm();
        $usernameForm->handleRequest($request);

        $results = [];
        $member = null;
        if ($usernameForm->isSubmitted() && $usernameForm->isValid()) {
            $data = $usernameForm->getData();
            /** @var MemberRepository $memberRepository */
            $memberRepository = $entityManager->getRepository(Member::class);
            /** @var Member $member */
            $member = $memberRepository->findOneBy(['username' => $data['username']]);

            /** @var MessageRepository $messageRepository */
            $messageRepository = $entityManager->getRepository(Message::class);
            $messages = $messageRepository->findAllMessagesWithMember($member);

            // Work through all messages and create list of members involved
            foreach ($messages as $message) {
                $sender = $message->getSender();
                $receiver = $message->getReceiver();
                $correspondent = ($sender === $member) ? $receiver : $sender;
                $username = $correspondent->getUsername();
                $type = $this->getConversationType($message);
                if (!\array_key_exists($username, $results)) {
                    $results[$username] = [
                        'type' => $type,
                        'username' => $username,
                        'direction' => 0,
                        'last_sent' => null,
                        'last_received' => null,
                    ];
                }
                $result = $results[$username];
                $result['type'] |= $type;
                if ($sender !== $member) {
                    $result['direction'] |= 1;
                    if ($message->getCreated() > $result['last_received']) {
                        $result['last_received'] = $message->getCreated();
                    }
                } else {
                    $result['direction'] |= 2;
                    if ($message->getCreated() > $result['last_sent']) {
                        $result['last_sent'] = $message->getCreated();
                    }
                }
                $results[$username] = $result;
            }
        }

        return $this->render(
            'admin/tools/messages.by.member.html.twig',
            [
                'form' => $usernameForm->createView(),
                'member' => $member,
                'results' => $results,
                'submenu' => [
                    'items' => $subMenuItems,
                    'active' => self::MESSAGES_BY_MEMBER,
                ],
            ]
        );
    }

    #[Route(path: '/admin/tools/countryage', name: 'admin_tools_age_by_country')]
    public function showAverageAgePerCountryAction(Request $request, EntityManagerInterface $entityManager): Response
    {
        // check permissions
        $subMenuItems = $this->checkPermissions($request, self::AGE_BY_COUNTRY);

        $connection = $entityManager->getConnection();
        $results = $connection->executeQuery("
            SELECT
                gc.Name AS Name,
                COUNT(*) AS Count,
                ROUND(AVG(m.BirthDate) / 10000) AS BirthYear,
                DATE_FORMAT(NOW(), '%Y') - ROUND(AVG(m.BirthDate) / 10000) AS 'Age'
            FROM
                members m,
                geonames g,
                geonamescountries gc
            WHERE
                m.Status = 'Active'
                AND m.IdCity = g.geonameId
                AND g.country = gc.country
            GROUP BY g.country
            ORDER BY 2 DESC;
        ")->fetchAllAssociative();

        return $this->render(
            'admin/tools/age.country.html.twig',
            [
                'results' => $results,
                'submenu' => [
                    'items' => $subMenuItems,
                    'active' => self::AGE_BY_COUNTRY,
                ],
            ]
        );
    }

    #[Route(path: '/admin/tools/uploaded_images/{page}', name: 'admin_tools_uploaded_images')]
    public function showUploadedImages(Request $request, EntityManagerInterface $entityManager, int $page = 1): Response
    {
        // check permissions
        $subMenuItems = $this->checkPermissions($request, self::UPLOADED_IMAGES);

        $repository = $entityManager->getRepository(UploadedImage::class);
        $qb = $repository->createQueryBuilder('i');
        $query = $qb->orderBy('i.created', 'desc');
        $adapter = new QueryAdapter($query);
        $pagerfanta = new Pagerfanta($adapter);
        $pagerfanta->setMaxPerPage(100);
        $pagerfanta->setCurrentPage($page);

        return $this->render(
            'admin/tools/uploaded.images.html.twig',
            [
                'results' => $pagerfanta,
                'submenu' => [
                    'items' => $subMenuItems,
                    'active' => self::AGE_BY_COUNTRY,
                ],
            ]
        );
    }

    #[Route(path: '/admin/tools/login_message/add', name: 'admin_tools_login_message_add')]
    public function addLoginMessage(Request $request, EntityManagerInterface $entityManager): Response
    {
        // check permissions
        $subMenuItems = $this->checkPermissions($request, self::LOGIN_MESSAGE_ADD);

        $form = $this->createForm(LoginMessageType::class);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $loginMessage = $form->getData();
            $entityManager->persist($loginMessage);
            $entityManager->flush();

            return $this->redirectToRoute('homepage');
        }

        return $this->render(
            'admin/tools/login.message.html.twig',
            [
                'form' => $form->createView(),
                'submenu' => [
                    'items' => $subMenuItems,
                    'active' => self::LOGIN_MESSAGE_ADD,
                ],
            ]
        );
    }

    #[Route(path: '/admin/tools/login_messages/show', name: 'admin_tools_login_messages_show')]
    public function showLoginMessages(Request $request, EntityManagerInterface $entityManager): Response
    {
        // check permissions
        $subMenuItems = $this->checkPermissions($request, self::LOGIN_MESSAGES_SHOW);

        $loginMessageRepository = $entityManager->getRepository(LoginMessage::class);
        $loginMessages = $loginMessageRepository->findBy([], ['expires' => 'DESC', 'id' => 'DESC']);

        return $this->render(
            'admin/tools/login.messages.show.html.twig',
            [
                'login_messages' => $loginMessages,
                'submenu' => [
                    'items' => $subMenuItems,
                    'active' => self::LOGIN_MESSAGES_SHOW,
                ],
            ]
        );
    }

    #[Route(path: '/admin/tools/login_message/{id}/expire', name: 'admin_tools_login_message_expire')]
    public function expireLoginMessage(LoginMessage $loginMessage, EntityManagerInterface $entityManager): Response
    {
        if (!$this->isGranted(Member::ROLE_ADMIN_ADMIN)) {
            return $this->redirectToRoute('homepage');
        }

        $loginMessage->setExpires(new DateTime());
        $entityManager->persist($loginMessage);
        $entityManager->flush();

        return $this->redirectToRoute('admin_tools_login_messages_show');
    }

    private function getSubMenuItems(): array
    {
        $subMenu = [];
        if (
            $this->isGranted(Member::ROLE_ADMIN_SAFETYTEAM)
            || $this->isGranted(Member::ROLE_ADMIN_PROFILE)
        ) {
            $subMenu[self::CHANGE_USERNAME] = [
                'key' => self::CHANGE_USERNAME,
                'url' => $this->generateUrl('admin_tools_change_username'),
            ];
            $subMenu[self::FIND_USER] = [
                'key' => self::FIND_USER,
                'url' => $this->generateUrl('admin_tools_find_user'),
            ];
            $subMenu[self::MESSAGES_SENT] = [
                'key' => self::MESSAGES_SENT,
                'url' => $this->generateUrl('admin_tools_messages_sent'),
            ];
            $subMenu[self::REQUESTS_SENT] = [
                'key' => self::REQUESTS_SENT,
                'url' => $this->generateUrl('admin_tools_requests_sent'),
            ];
        }

        if ($this->isGranted(Member::ROLE_ADMIN_SAFETYTEAM)) {
            $subMenu[self::MESSAGES_BY_MEMBER] = [
                'key' => self::MESSAGES_BY_MEMBER,
                'url' => $this->generateUrl('admin_tools_messages_by_member'),
            ];
        }

        if ($this->isGranted(Member::ROLE_ADMIN_ACCEPTER)) {
            $subMenu[self::FIND_USER] = [
                'key' => self::FIND_USER,
                'url' => $this->generateUrl('admin_tools_find_user'),
            ];
            $subMenu[self::AGE_BY_COUNTRY] = [
                'key' => self::AGE_BY_COUNTRY,
                'url' => $this->generateUrl('admin_tools_age_by_country'),
            ];
        }

        if ($this->isGranted(Member::ROLE_ADMIN_CHECKER)) {
            $subMenu[self::CHECK_FEEDBACK] = [
                'key' => self::CHECK_FEEDBACK,
                'url' => $this->generateUrl('admin_tools_check_feedback'),
            ];
            $subMenu[self::CHECK_TOP_SPAMMER] = [
                'key' => self::CHECK_TOP_SPAMMER,
                'url' => $this->generateUrl('admin_tools_top_spammer'),
            ];
            $subMenu[self::DAMAGE_DONE] = [
                'key' => self::DAMAGE_DONE,
                'url' => $this->generateUrl('admin_tools_damage_done'),
            ];
            $subMenu[self::AGE_BY_COUNTRY] = [
                'key' => self::AGE_BY_COUNTRY,
                'url' => $this->generateUrl('admin_tools_age_by_country'),
            ];
        }

        if ($this->isGranted(Member::ROLE_ADMIN_ADMIN)) {
            $subMenu[self::CHECK_FEEDBACK] = [
                'key' => self::CHECK_FEEDBACK,
                'url' => $this->generateUrl('admin_tools_check_feedback'),
            ];
            $subMenu[self::AGE_BY_COUNTRY] = [
                'key' => self::AGE_BY_COUNTRY,
                'url' => $this->generateUrl('admin_tools_age_by_country'),
            ];
            $subMenu[self::UPLOADED_IMAGES] = [
                'key' => self::UPLOADED_IMAGES,
                'url' => $this->generateUrl('admin_tools_uploaded_images'),
            ];
            $subMenu[self::LOGIN_MESSAGES_SHOW] = [
                'key' => self::LOGIN_MESSAGES_SHOW,
                'url' => $this->generateUrl('admin_tools_login_messages_show'),
            ];
            $subMenu[self::LOGIN_MESSAGE_ADD] = [
                'key' => self::LOGIN_MESSAGE_ADD,
                'url' => $this->generateUrl('admin_tools_login_message_add'),
            ];
        }

        return $subMenu;
    }

    /**
     * @return RedirectResponse|array
     */
    private function checkPermissions(Request $request, ?string $tool = null)
    {
        // check permissions
        $subMenuItems = $this->getSubMenuItems();

        if (empty($subMenuItems) || ((null !== $tool) && !\array_key_exists($tool, $subMenuItems))) {
            $this->addFlash('notice', 'admin.tools.not.allowed');
            $referrer = $request->headers->get('referer');

            return $this->redirect($referrer);
        }

        return $subMenuItems;
    }

    private function getConversationType(Message $message)
    {
        if (null === $message->getRequest()) {
            $type = 1;
        } elseif (null !== $message->getRequest()->getInviteForLeg()) {
            $type = 2; // Invitation
        } else {
            $type = 4;
        }

        return $type;
    }
}
