<?php

declare(strict_types=1);

namespace App\Controller;

use App\Entity\Comment;
use App\Entity\Document;
use App\Enum\DocumentType;
use App\Repository\ProfileRepository;
use App\Service\DocumentStorageService;
use DateTimeImmutable;
use DateTimeInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;

#[Route('/api/comments')]
final class CommentController extends AbstractController
{
    public function __construct(
        private readonly EntityManagerInterface $entityManager,
        private readonly ProfileRepository $profiles,
        private readonly DocumentStorageService $storageService,
    ) {}

    #[Route('', name: 'api_comments_create', methods: ['POST'])]
    public function create(Request $request): JsonResponse
    {
        $this->denyAccessUnlessGranted('ROLE_VIEWER');

        $session   = $request->getSession();
        $profileId = $session->get('profileId');
        if (!$profileId) {
            return $this->json(['message' => 'Aucun profil actif.'], 401);
        }

        $profile = $this->profiles->find($profileId);
        if (!$profile) {
            return $this->json(['message' => 'Profil introuvable.'], 401);
        }

        // Parse fields from JSON or form-data
        $contentType = $request->headers->get('Content-Type', '');
        $isFormData  = str_contains($contentType, 'multipart/form-data') || $request->files->count() > 0 || $request->request->has('content');
        if ($isFormData) {
            $content    = trim((string) $request->request->get('content', ''));
            $entityType = trim((string) $request->request->get('entityType', ''));
            $entityId   = trim((string) $request->request->get('entityId', ''));
            $entityName = $request->request->get('entityName') ? trim((string) $request->request->get('entityName')) : null;
        } else {
            $payload = json_decode($request->getContent(), true);
            if (!is_array($payload)) {
                return $this->json(['message' => 'Payload JSON invalide.'], 400);
            }
            $content    = trim((string) ($payload['content'] ?? ''));
            $entityType = trim((string) ($payload['entityType'] ?? ''));
            $entityId   = trim((string) ($payload['entityId'] ?? ''));
            $entityName = isset($payload['entityName']) ? trim((string) $payload['entityName']) : null;
        }

        if ('' === $content) {
            return $this->json(['message' => 'Le contenu est requis.'], 400);
        }

        $allowedTypes = ['machine', 'piece', 'composant', 'product', 'piece_category', 'component_category', 'product_category', 'machine_skeleton'];
        if (!in_array($entityType, $allowedTypes, true)) {
            return $this->json(['message' => 'Type d\'entité invalide.'], 400);
        }

        if ('' === $entityId) {
            return $this->json(['message' => 'L\'identifiant de l\'entité est requis.'], 400);
        }

        $authorName = trim(sprintf('%s %s', $profile->getFirstName(), $profile->getLastName()));
        if ('' === $authorName) {
            $authorName = $profile->getEmail() ?? 'Inconnu';
        }

        $comment = new Comment();
        $comment->setContent($content);
        $comment->setEntityType($entityType);
        $comment->setEntityId($entityId);
        $comment->setEntityName($entityName);
        $comment->setAuthorId($profileId);
        $comment->setAuthorName($authorName);

        $this->entityManager->persist($comment);

        // Handle file uploads
        $allowedMimeTypes = [
            'application/pdf',
            'image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/bmp',
            'text/plain', 'text/csv',
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            'application/msword', 'application/vnd.ms-excel',
            'application/zip',
        ];

        $files = $request->files->all('files');
        foreach ($files as $file) {
            if (!$file instanceof UploadedFile || !$file->isValid()) {
                continue;
            }

            $detectedMime = $file->getMimeType() ?: 'application/octet-stream';
            if (!in_array($detectedMime, $allowedMimeTypes, true)) {
                return $this->json([
                    'message' => sprintf('Type de fichier non autorisé : %s', $detectedMime),
                ], 400);
            }

            $document   = new Document();
            $documentId = 'cl'.bin2hex(random_bytes(12));
            $document->setId($documentId);
            $document->setName($file->getClientOriginalName());
            $document->setFilename($file->getClientOriginalName());
            $document->setMimeType($file->getMimeType() ?: 'application/octet-stream');
            $document->setSize((int) $file->getSize());
            $document->setType(DocumentType::DOCUMENTATION);
            $document->setComment($comment);
            $comment->getDocuments()->add($document);

            $extension    = $this->storageService->extensionFromFilename($file->getClientOriginalName());
            $relativePath = $this->storageService->storeFromPath(
                $file->getPathname(),
                $documentId,
                $extension,
            );
            $document->setPath($relativePath);

            $this->entityManager->persist($document);
        }

        $this->entityManager->flush();

        return $this->json($this->normalize($comment), 201);
    }

    #[Route('/{id}/resolve', name: 'api_comments_resolve', methods: ['PATCH'])]
    public function resolve(string $id, Request $request): JsonResponse
    {
        $this->denyAccessUnlessGranted('ROLE_GESTIONNAIRE');

        $comment = $this->entityManager->getRepository(Comment::class)->find($id);
        if (!$comment) {
            return $this->json(['message' => 'Commentaire introuvable.'], 404);
        }

        $session   = $request->getSession();
        $profileId = $session->get('profileId');
        $profile   = $profileId ? $this->profiles->find($profileId) : null;

        $resolverName = 'Inconnu';
        if ($profile) {
            $resolverName = trim(sprintf('%s %s', $profile->getFirstName(), $profile->getLastName()));
            if ('' === $resolverName) {
                $resolverName = $profile->getEmail() ?? 'Inconnu';
            }
        }

        $comment->setStatus('resolved');
        $comment->setResolvedById($profileId);
        $comment->setResolvedByName($resolverName);
        $comment->setResolvedAt(new DateTimeImmutable());

        $this->entityManager->flush();

        return $this->json($this->normalize($comment));
    }

    #[Route('/search/list', name: 'api_comments_list', methods: ['GET'])]
    public function list(Request $request): JsonResponse
    {
        $this->denyAccessUnlessGranted('ROLE_VIEWER');

        $qb = $this->entityManager->getRepository(Comment::class)->createQueryBuilder('c');

        $status = $request->query->get('status');
        if ($status) {
            $qb->andWhere('c.status = :status')->setParameter('status', $status);
        }

        $entityType = $request->query->get('entityType');
        if ($entityType) {
            $qb->andWhere('c.entityType = :entityType')->setParameter('entityType', $entityType);
        }

        $entityName = $request->query->get('entityName');
        if ($entityName) {
            $qb->andWhere('LOWER(c.entityName) LIKE LOWER(:entityName)')->setParameter('entityName', '%'.$entityName.'%');
        }

        // Count total before pagination
        $countQb = clone $qb;
        $total   = (int) $countQb->select('COUNT(c.id)')->getQuery()->getSingleScalarResult();

        // Sorting
        $sortField         = $request->query->get('sort', 'createdAt');
        $sortDir           = strtoupper($request->query->get('direction', 'DESC'));
        $allowedSortFields = ['createdAt', 'authorName', 'status'];
        if (!in_array($sortField, $allowedSortFields, true)) {
            $sortField = 'createdAt';
        }
        if (!in_array($sortDir, ['ASC', 'DESC'], true)) {
            $sortDir = 'DESC';
        }
        $qb->orderBy('c.'.$sortField, $sortDir);

        // Pagination
        $itemsPerPage = min((int) $request->query->get('itemsPerPage', '30'), 200);
        $page         = max((int) $request->query->get('page', '1'), 1);
        $qb->setMaxResults($itemsPerPage)->setFirstResult(($page - 1) * $itemsPerPage);

        $comments = $qb->getQuery()->getResult();

        return $this->json([
            'items' => array_map(fn (Comment $c) => $this->normalize($c), $comments),
            'total' => $total,
        ]);
    }

    #[Route('/by-entity/{entityType}/{entityId}', name: 'api_comments_by_entity', methods: ['GET'])]
    public function listByEntity(string $entityType, string $entityId, Request $request): JsonResponse
    {
        $this->denyAccessUnlessGranted('ROLE_VIEWER');

        $criteria = ['entityType' => $entityType, 'entityId' => $entityId];

        $status = $request->query->get('status');
        if ($status) {
            $criteria['status'] = $status;
        }

        $comments = $this->entityManager->getRepository(Comment::class)
            ->findBy($criteria, ['createdAt' => 'DESC'])
        ;

        return $this->json(array_map(fn (Comment $c) => $this->normalize($c), $comments));
    }

    #[Route('/stats/unresolved-count', name: 'api_comments_unresolved_count', methods: ['GET'])]
    public function unresolvedCount(): JsonResponse
    {
        $this->denyAccessUnlessGranted('ROLE_VIEWER');

        $count = $this->entityManager->getRepository(Comment::class)
            ->count(['status' => 'open'])
        ;

        return $this->json(['count' => $count]);
    }

    private function normalize(Comment $comment): array
    {
        $documents = [];
        foreach ($comment->getDocuments() as $document) {
            $documents[] = [
                'id'          => $document->getId(),
                'name'        => $document->getName(),
                'filename'    => $document->getFilename(),
                'mimeType'    => $document->getMimeType(),
                'size'        => $document->getSize(),
                'type'        => $document->getType()->value,
                'fileUrl'     => '/api/documents/'.$document->getId().'/file',
                'downloadUrl' => '/api/documents/'.$document->getId().'/download',
                'createdAt'   => $document->getCreatedAt()->format(DateTimeInterface::ATOM),
            ];
        }

        return [
            'id'             => $comment->getId(),
            'content'        => $comment->getContent(),
            'entityType'     => $comment->getEntityType(),
            'entityId'       => $comment->getEntityId(),
            'entityName'     => $comment->getEntityName(),
            'authorId'       => $comment->getAuthorId(),
            'authorName'     => $comment->getAuthorName(),
            'status'         => $comment->getStatus(),
            'resolvedById'   => $comment->getResolvedById(),
            'resolvedByName' => $comment->getResolvedByName(),
            'resolvedAt'     => $comment->getResolvedAt()?->format(DateTimeInterface::ATOM),
            'createdAt'      => $comment->getCreatedAt()->format(DateTimeInterface::ATOM),
            'updatedAt'      => $comment->getUpdatedAt()->format(DateTimeInterface::ATOM),
            'documents'      => $documents,
        ];
    }
}
