<?php

declare(strict_types=1);

namespace MauticPlugin\DealFlowBundle\Controller;

use Mautic\CoreBundle\Controller\CommonController;
use MauticPlugin\DealFlowBundle\Entity\Deal;
use MauticPlugin\DealFlowBundle\Entity\DealRepository;
use MauticPlugin\DealFlowBundle\Entity\DealStageRepository;
use MauticPlugin\DealFlowBundle\Service\DealAuthorization;
use MauticPlugin\DealFlowBundle\Service\DealManager;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;

/**
 * Handles bulk deal operations (batch archive, stage change, undo).
 * Consumed by DealController (facade) — do not register routes here directly.
 */
final class DealBatchController extends CommonController
{
    public function batchAction(
        Request $request,
        DealAuthorization $authorization,
        DealRepository $dealRepository,
        DealStageRepository $stageRepository,
        DealManager $dealManager,
        CsrfTokenManagerInterface $csrfTokenManager,
    ): Response {
        if (!$authorization->canViewDeals()) {
            return $this->accessDenied();
        }

        $this->validateCsrfToken($csrfTokenManager, 'dealflow_batch', (string) $request->request->get('_token'));

        $action = (string) $request->request->get('batch_action');
        /** @var list<string> $rawIds */
        $rawIds = (array) $request->request->all('deal_ids');
        $ids = array_filter(array_map('intval', $rawIds), static fn (int $id): bool => $id > 0);

        $returnUrl = (string) $request->request->get('return_url', $this->generateUrl('mautic_dealflow_index'));

        if ([] === $ids || '' === $action) {
            $this->addFlash('error', 'No deals selected.');

            return $this->delegateRedirect($returnUrl);
        }

        $deals = $dealRepository->findBy(['id' => $ids]);

        switch ($action) {
            case 'delete':
                $permittedDeals = array_values(array_filter($deals, static fn (Deal $deal): bool => $authorization->canDeleteDeal($deal)));

                if ([] === $permittedDeals) {
                    $this->addFlash('error', 'You do not have permission to archive these deals.');

                    return $this->delegateRedirect($returnUrl);
                }

                $count = $dealManager->archiveDeals($permittedDeals, $this->user?->getId(), 'ui');

                $archivedIds = array_map(static fn (Deal $d): int => (int) $d->getId(), $permittedDeals);
                $request->getSession()->set('dealflow_undo_archived_ids', $archivedIds);
                $request->getSession()->set('dealflow_undo_timestamp', time());

                $this->addFlash('notice', sprintf('%d deal(s) archived.', $count));

                break;

            case 'changeStage':
                $stageId = (int) $request->request->get('batch_stage_id');
                $stage = $stageRepository->find($stageId);

                if (null === $stage) {
                    $this->addFlash('error', 'Invalid stage selected.');

                    return $this->delegateRedirect($returnUrl);
                }

                $permittedDeals = array_values(array_filter($deals, static fn (Deal $deal): bool => $authorization->canEditDeal($deal)));

                if ([] === $permittedDeals) {
                    $this->addFlash('error', 'You do not have permission to edit these deals.');

                    return $this->delegateRedirect($returnUrl);
                }

                $count = $dealManager->batchChangeStage($permittedDeals, $stage, $this->user?->getId(), 'ui');
                $this->addFlash('notice', sprintf('%d deal(s) moved to %s.', $count, $stage->getName()));

                break;

            default:
                $this->addFlash('error', 'Unknown batch action.');
        }

        return $this->delegateRedirect($returnUrl);
    }

    public function undoBatchAction(
        Request $request,
        DealAuthorization $authorization,
        DealRepository $dealRepository,
        DealManager $dealManager,
        CsrfTokenManagerInterface $csrfTokenManager,
    ): Response {
        if (!$authorization->canViewDeals()) {
            return $this->accessDenied();
        }

        $this->validateCsrfToken($csrfTokenManager, 'dealflow_undo', (string) $request->request->get('_token'));

        $session = $request->getSession();
        /** @var list<int> $archivedIds */
        $archivedIds = (array) $session->get('dealflow_undo_archived_ids', []);
        $timestamp = (int) $session->get('dealflow_undo_timestamp', 0);

        $session->remove('dealflow_undo_archived_ids');
        $session->remove('dealflow_undo_timestamp');

        $returnUrl = (string) $request->request->get('return_url', $this->generateUrl('mautic_dealflow_index'));

        if ([] === $archivedIds || (time() - $timestamp) > 60) {
            $this->addFlash('error', 'Undo window has expired.');

            return $this->delegateRedirect($returnUrl);
        }

        $deals = $dealRepository->findBy(['id' => $archivedIds]);
        $permittedDeals = array_values(array_filter($deals, static fn (Deal $d): bool => $authorization->canDeleteDeal($d)));
        $count = $dealManager->unarchiveDeals($permittedDeals, $this->user?->getId(), 'ui');

        $this->addFlash('notice', sprintf('%d deal(s) restored.', $count));

        return $this->delegateRedirect($returnUrl);
    }

    private function validateCsrfToken(CsrfTokenManagerInterface $csrfTokenManager, string $tokenId, string $submittedToken): void
    {
        if (!$csrfTokenManager->isTokenValid(new CsrfToken($tokenId, $submittedToken))) {
            throw $this->createAccessDeniedException('Invalid CSRF token.');
        }
    }
}
