<?php

declare(strict_types=1);

namespace App\Module\ProjectManagement\Infrastructure\Mcp\Tool\Task;

use App\Module\ProjectManagement\Domain\Entity\TaskDocument;
use Doctrine\ORM\EntityManagerInterface;
use InvalidArgumentException;
use Mcp\Capability\Attribute\McpTool;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;

use function sprintf;
use function strlen;

#[McpTool(name: 'update-task-document', description: 'Update a document attached to a task: replace its text content and/or rename it. Pass the new raw content (verbatim UTF-8) and/or a new fileName. The MIME type is re-inferred from the fileName extension. At least one of content or fileName must be provided.')]
class UpdateTaskDocumentTool
{
    private const MAX_CONTENT_SIZE = 5 * 1024 * 1024; // 5 MB of text

    private const EXTENSION_TO_MIME = [
        'md'       => 'text/markdown',
        'markdown' => 'text/markdown',
        'txt'      => 'text/plain',
        'csv'      => 'text/csv',
        'json'     => 'application/json',
        'xml'      => 'text/xml',
    ];

    public function __construct(
        private readonly EntityManagerInterface $entityManager,
        private readonly Security $security,
        private readonly string $uploadDir,
    ) {}

    /**
     * @param int         $id       ID of the task document to update
     * @param null|string $content  New raw text content of the document (e.g. Markdown). Omit to keep the current content.
     * @param null|string $fileName New display name of the document, including extension. Omit to keep the current name.
     */
    public function __invoke(
        int $id,
        ?string $content = null,
        ?string $fileName = null,
    ): string {
        if (!$this->security->isGranted('ROLE_USER')) {
            throw new AccessDeniedException('Access denied: ROLE_USER required.');
        }

        if (null === $content && null === $fileName) {
            throw new InvalidArgumentException('At least one of content or fileName must be provided.');
        }

        $document = $this->entityManager->find(TaskDocument::class, $id);
        if (null === $document) {
            throw new InvalidArgumentException(sprintf('Task document with ID %d not found.', $id));
        }

        // Rename: update the display name and re-infer the MIME type from its extension.
        if (null !== $fileName) {
            $originalName = trim($fileName);
            if ('' === $originalName) {
                throw new InvalidArgumentException('fileName cannot be empty.');
            }

            $extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
            if ('' === $extension) {
                $originalName .= '.md';
                $extension = 'md';
            }

            $document->setOriginalName($originalName);
            $document->setMimeType(self::EXTENSION_TO_MIME[$extension] ?? 'text/markdown');
        }

        // Replace content: overwrite the stored file in place and refresh its size.
        if (null !== $content) {
            if ('' === $content) {
                throw new InvalidArgumentException('Document content cannot be empty.');
            }

            $size = strlen($content);
            if ($size > self::MAX_CONTENT_SIZE) {
                throw new InvalidArgumentException('Content size exceeds 5 MB limit.');
            }

            $filePath = $this->uploadDir.'/'.$document->getFileName();
            if (false === file_put_contents($filePath, $content)) {
                throw new InvalidArgumentException('Failed to write document to disk.');
            }

            $document->setSize($size);
        }

        $this->entityManager->flush();

        return json_encode([
            'id'           => $document->getId(),
            'taskId'       => $document->getTask()?->getId(),
            'originalName' => $document->getOriginalName(),
            'mimeType'     => $document->getMimeType(),
            'size'         => $document->getSize(),
            'createdAt'    => $document->getCreatedAt()?->format('c'),
            'uploadedBy'   => $document->getUploadedBy()?->getUsername(),
        ]);
    }
}
