<?php

declare(strict_types=1);

namespace MauticPlugin\DealFlowBundle\Controller;

use Mautic\CoreBundle\Controller\CommonController;
use MauticPlugin\DealFlowBundle\Entity\Deal;
use MauticPlugin\DealFlowBundle\Entity\DealAttachmentRepository;
use MauticPlugin\DealFlowBundle\Entity\DealRepository;
use MauticPlugin\DealFlowBundle\Service\AttachmentStorage;
use MauticPlugin\DealFlowBundle\Service\DealAuthorization;
use MauticPlugin\DealFlowBundle\Service\DealManager;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
use Symfony\Component\HttpFoundation\HeaderUtils;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;

/**
 * Handles deal attachment upload, download, and delete.
 * Consumed by DealController (facade) — do not register routes here directly.
 */
final class DealAttachmentController extends CommonController
{
    public function addAttachmentAction(
        int $objectId,
        Request $request,
        DealAuthorization $authorization,
        DealRepository $dealRepository,
        DealManager $dealManager,
        CsrfTokenManagerInterface $csrfTokenManager,
    ): Response {
        $deal = $dealRepository->find($objectId);
        if (!$deal instanceof Deal) {
            throw $this->createNotFoundException(sprintf('Deal %d not found.', $objectId));
        }

        if (!$authorization->canEditDeal($deal)) {
            return $this->accessDenied();
        }

        $this->validateCsrfToken($csrfTokenManager, sprintf('dealflow_add_attachment_%d', $deal->getId()), (string) $request->request->get('_token'));

        $file = $request->files->get('attachment');
        if (null !== $file) {
            try {
                $dealManager->addAttachment($deal, $file, $this->user?->getId(), 'ui');
                $this->addFlash('notice', 'Attachment uploaded.');
            } catch (\Throwable $exception) {
                $this->addFlash('error', $exception->getMessage());
            }
        }

        return $this->delegateRedirect($this->generateUrl('mautic_dealflow_view', ['objectId' => $objectId]));
    }

    public function downloadAttachmentAction(
        int $attachmentId,
        DealAuthorization $authorization,
        DealAttachmentRepository $attachmentRepository,
        AttachmentStorage $attachmentStorage,
    ): BinaryFileResponse {
        $attachment = $attachmentRepository->find($attachmentId);
        if (null === $attachment) {
            throw $this->createNotFoundException(sprintf('Attachment %d not found.', $attachmentId));
        }

        $deal = $attachment->getDeal();
        if (!$deal instanceof Deal || !$authorization->canViewDeal($deal)) {
            throw new \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException();
        }

        $path = $attachmentStorage->getAbsolutePath($attachment->getStoragePath());
        if (!is_file($path)) {
            throw new FileNotFoundException($path);
        }

        $response = new BinaryFileResponse($path);
        $disposition = HeaderUtils::makeDisposition(HeaderUtils::DISPOSITION_ATTACHMENT, $attachment->getOriginalFilename());
        $response->headers->set('Content-Disposition', $disposition);

        return $response;
    }

    public function deleteAttachmentAction(
        int $objectId,
        int $attachmentId,
        Request $request,
        DealAuthorization $authorization,
        DealRepository $dealRepository,
        DealAttachmentRepository $attachmentRepository,
        DealManager $dealManager,
        CsrfTokenManagerInterface $csrfTokenManager,
    ): Response {
        $deal = $dealRepository->find($objectId);
        if (!$deal instanceof Deal) {
            throw $this->createNotFoundException(sprintf('Deal %d not found.', $objectId));
        }

        if (!$authorization->canEditDeal($deal)) {
            return $this->accessDenied();
        }

        $attachment = $attachmentRepository->find($attachmentId);
        if (null === $attachment || $attachment->getDeal()?->getId() !== $deal->getId()) {
            throw $this->createNotFoundException(sprintf('Attachment %d not found for deal %d.', $attachmentId, $objectId));
        }

        $this->validateCsrfToken($csrfTokenManager, sprintf('dealflow_delete_attachment_%d', $attachment->getId()), (string) $request->request->get('_token'));

        $dealManager->deleteAttachment($attachment, $this->user?->getId(), 'ui');
        $this->addFlash('notice', 'Attachment deleted.');

        return $this->delegateRedirect($this->generateUrl('mautic_dealflow_view', ['objectId' => $deal->getId()]));
    }

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