<?php

declare(strict_types=1);

namespace MauticPlugin\DealFlowBundle\Controller;

use Mautic\CoreBundle\Controller\CommonController;
use MauticPlugin\DealFlowBundle\Controller\Trait\QueryParamTrait;
use MauticPlugin\DealFlowBundle\Entity\DealRepository;
use MauticPlugin\DealFlowBundle\Entity\DealStageRepository;
use MauticPlugin\DealFlowBundle\Service\DealAuthorization;
use MauticPlugin\DealFlowBundle\Service\DealContextBuilder;
use MauticPlugin\DealFlowBundle\Service\DealFieldManager;
use MauticPlugin\DealFlowBundle\Service\DefaultPipelineInitializer;
use MauticPlugin\DealFlowBundle\Service\HealthIndicatorCalculator;
use MauticPlugin\DealFlowBundle\Service\PipelineVelocityCalculator;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;

/**
 * Handles deal list and pipeline kanban views.
 * Consumed by DealController (facade) — do not register routes here directly.
 */
final class DealListController extends CommonController
{
    use QueryParamTrait;

    public function pipelineAction(
        Request $request,
        DealAuthorization $authorization,
        DefaultPipelineInitializer $pipelineInitializer,
        DealRepository $dealRepository,
        DealContextBuilder $contextBuilder,
        HealthIndicatorCalculator $healthIndicator,
        PipelineVelocityCalculator $velocityCalculator,
    ): Response {
        if (!$authorization->canViewDeals()) {
            return $this->accessDenied();
        }

        $search = (string) $this->getRequestValue($request, 'search', '');
        $pipelineId = $this->queryNullableInt($request, 'pipelineId');
        $pipeline = $pipelineInitializer->resolvePipeline($pipelineId);
        $stages = $pipelineInitializer->getOrderedStages($pipeline, true);
        $deals = $dealRepository->findForPipeline($pipeline, ['search' => $search], $authorization->getCurrentUserId(), $authorization->canViewOtherDeals());
        $contexts = $contextBuilder->buildForDeals($deals);
        $columns = [];
        $editableDealIds = [];
        $healthIndicators = [];

        foreach ($deals as $deal) {
            $editableDealIds[$deal->getId() ?? spl_object_id($deal)] = $authorization->canEditDeal($deal);
            $stageEnteredAt = $deal->getDateModified();
            if ($stageEnteredAt instanceof \DateTimeInterface) {
                $healthIndicators[$deal->getId()] = $healthIndicator->calculate($stageEnteredAt);
            }
        }

        foreach ($stages as $stage) {
            $columns[$stage->getId() ?? spl_object_id($stage)] = [
                'stage' => $stage,
                'deals' => [],
            ];
        }

        foreach ($deals as $deal) {
            $stageId = $deal->getStage()?->getId();
            if (null === $stageId || !isset($columns[$stageId])) {
                continue;
            }

            $columns[$stageId]['deals'][] = $deal;
        }

        $totalPipelineValue = $dealRepository->getPipelineValue($pipeline);
        $pipelineVelocity = $velocityCalculator->calculate($pipeline);

        return $this->delegateView([
            'viewParameters' => [
                'pipelines' => $pipelineInitializer->getActivePipelines(),
                'selectedPipeline' => $pipeline,
                'columns' => $columns,
                'dealContexts' => $contexts,
                'stages' => $stages,
                'searchValue' => $search,
                'editableDealIds' => $editableDealIds,
                'dealHealthIndicators' => $healthIndicators,
                'totalPipelineValue' => $totalPipelineValue,
                'pipelineVelocity' => $pipelineVelocity,
            ],
            'contentTemplate' => '@DealFlow/Deal/pipeline.html.twig',
            'passthroughVars' => [
                'activeLink' => '#mautic_dealflow_pipeline',
                'route' => $this->generateUrl('mautic_dealflow_pipeline', ['pipelineId' => $pipeline->getId()]),
                'mauticContent' => 'dealflow',
            ],
        ]);
    }

    public function indexAction(
        Request $request,
        DealAuthorization $authorization,
        DefaultPipelineInitializer $pipelineInitializer,
        DealRepository $dealRepository,
        DealContextBuilder $contextBuilder,
        DealStageRepository $stageRepository,
        DealFieldManager $dealFieldManager,
        CsrfTokenManagerInterface $csrfTokenManager,
        int $page = 1,
    ): Response {
        if (!$authorization->canViewDeals()) {
            return $this->accessDenied();
        }

        $pipelineId = $this->queryNullableInt($request, 'pipelineId');
        $pipeline = $pipelineInitializer->resolvePipeline($pipelineId);
        $activeStage = null;
        $stageId = $this->getRequestValue($request, 'stageId');
        $search = $this->getRequestValue($request, 'search');
        $status = $this->getRequestValue($request, 'status');
        $ownerId = $this->getRequestValue($request, 'ownerId');
        $companyId = $this->getRequestValue($request, 'companyId');

        if (null !== $stageId && '' !== (string) $stageId) {
            $activeStage = $stageRepository->find((int) $stageId);
            if ($activeStage instanceof \MauticPlugin\DealFlowBundle\Entity\DealStage && $activeStage->getPipeline()?->getId() !== $pipeline->getId()) {
                $activeStage = null;
                $stageId = null;
            }
        }

        $pageSize = 30;
        $filters = [
            'search' => $search,
            'status' => $status,
            'ownerId' => $ownerId,
            'companyId' => $companyId,
            'stageId' => $stageId,
        ];

        $totalCount = $dealRepository->countForPipeline(
            $pipeline,
            $filters,
            $authorization->getCurrentUserId(),
            $authorization->canViewOtherDeals(),
        );
        $totalPages = max(1, (int) ceil($totalCount / $pageSize));
        $page = max(1, min($page, $totalPages));

        $deals = $dealRepository->findForPipelinePaginated(
            $pipeline,
            $filters,
            $authorization->getCurrentUserId(),
            $authorization->canViewOtherDeals(),
            $page,
            $pageSize,
        );

        $session = $request->getSession();
        /** @var list<int> $undoIds */
        $undoIds = (array) $session->get('dealflow_undo_archived_ids', []);
        $undoTimestamp = (int) $session->get('dealflow_undo_timestamp', 0);
        $undoCount = ([] !== $undoIds && (time() - $undoTimestamp) <= 60) ? count($undoIds) : 0;

        return $this->delegateView([
            'viewParameters' => [
                'pipelines' => $pipelineInitializer->getActivePipelines(),
                'selectedPipeline' => $pipeline,
                'items' => $deals,
                'dealContexts' => $contextBuilder->buildForDeals($deals),
                'listCustomFields' => $dealFieldManager->getListFields(),
                'customFieldListValues' => $dealFieldManager->buildListValueMap($deals),
                'page' => $page,
                'totalPages' => $totalPages,
                'totalCount' => $totalCount,
                'searchValue' => (string) ($search ?? ''),
                'activeStage' => $activeStage,
                'clearFiltersUrl' => $this->generateUrl('mautic_dealflow_index', ['page' => $page, 'pipelineId' => $pipeline->getId()]),
                'pipelineStages' => $pipelineInitializer->getOrderedStages($pipeline, true),
                'batchCsrfToken' => $csrfTokenManager->getToken('dealflow_batch')->getValue(),
                'undoCsrfToken' => $csrfTokenManager->getToken('dealflow_undo')->getValue(),
                'undoCount' => $undoCount,
            ],
            'contentTemplate' => '@DealFlow/Deal/list.html.twig',
            'passthroughVars' => [
                'activeLink' => '#mautic_dealflow_index',
                'route' => $this->generateUrl('mautic_dealflow_index', ['page' => $page, 'pipelineId' => $pipeline->getId()]),
                'mauticContent' => 'dealflow',
            ],
        ]);
    }

    private function getRequestValue(Request $request, string $key, mixed $default = null): mixed
    {
        if ($request->query->has($key)) {
            return $request->query->get($key);
        }

        if ($request->attributes->has($key)) {
            return $request->attributes->get($key);
        }

        return $default;
    }
}
