<?php

declare(strict_types=1);

namespace App\Module\Core\Infrastructure\Controller;

use App\Module\Core\Domain\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Uid\Uuid;

class UserAvatarController extends AbstractController
{
    private const MAX_FILE_SIZE      = 5 * 1024 * 1024; // 5 MB
    private const ALLOWED_MIME_TYPES = ['image/jpeg', 'image/png', 'image/webp', 'image/gif'];

    public function __construct(
        private readonly EntityManagerInterface $entityManager,
        private readonly string $avatarUploadDir,
    ) {}

    #[Route('/api/users/{id}/avatar', name: 'user_avatar_upload', methods: ['POST'], priority: 1)]
    #[IsGranted('IS_AUTHENTICATED_FULLY')]
    public function upload(int $id, Request $request): JsonResponse
    {
        $user = $this->findUserOrFail($id);
        $this->assertCanManageAvatar($user);

        $file = $request->files->get('file');

        if (null === $file || !$file->isValid()) {
            throw new BadRequestHttpException('No valid file uploaded.');
        }

        if ($file->getSize() > self::MAX_FILE_SIZE) {
            throw new BadRequestHttpException('File size exceeds 5 MB limit.');
        }

        $mimeType = $file->getMimeType();

        if (!in_array($mimeType, self::ALLOWED_MIME_TYPES, true)) {
            throw new BadRequestHttpException('Invalid file type. Allowed: JPEG, PNG, WebP, GIF.');
        }

        // Delete previous avatar file if exists
        $this->deleteAvatarFile($user);

        $extension = $file->guessExtension() ?? 'bin';
        $fileName  = Uuid::v4()->toRfc4122().'.'.$extension;

        if (!is_dir($this->avatarUploadDir)) {
            mkdir($this->avatarUploadDir, 0o775, true);
        }

        $file->move($this->avatarUploadDir, $fileName);

        $user->setAvatarFileName($fileName);
        $this->entityManager->flush();

        return new JsonResponse(['avatarUrl' => $user->getAvatarUrl()]);
    }

    #[Route('/api/users/{id}/avatar', name: 'user_avatar_serve', methods: ['GET'], priority: 1)]
    #[IsGranted('IS_AUTHENTICATED_FULLY')]
    public function serve(int $id): BinaryFileResponse
    {
        $user = $this->findUserOrFail($id);

        if (null === $user->getAvatarFileName()) {
            throw new NotFoundHttpException('No avatar set.');
        }

        $filePath = $this->avatarUploadDir.'/'.$user->getAvatarFileName();

        if (!file_exists($filePath)) {
            throw new NotFoundHttpException('Avatar file not found on disk.');
        }

        $response = new BinaryFileResponse($filePath);
        $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_INLINE, $user->getAvatarFileName());
        $extension = pathinfo($user->getAvatarFileName(), PATHINFO_EXTENSION);
        $mimeMap   = ['jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'png' => 'image/png', 'webp' => 'image/webp', 'gif' => 'image/gif'];
        $response->headers->set('Content-Type', $mimeMap[$extension] ?? 'application/octet-stream');
        $response->headers->set('Cache-Control', 'public, max-age=86400');

        return $response;
    }

    #[Route('/api/users/{id}/avatar', name: 'user_avatar_delete', methods: ['DELETE'], priority: 1)]
    #[IsGranted('IS_AUTHENTICATED_FULLY')]
    public function delete(int $id): Response
    {
        $user = $this->findUserOrFail($id);
        $this->assertCanManageAvatar($user);

        $this->deleteAvatarFile($user);
        $user->setAvatarFileName(null);
        $this->entityManager->flush();

        return new Response(null, Response::HTTP_NO_CONTENT);
    }

    private function findUserOrFail(int $id): User
    {
        $user = $this->entityManager->getRepository(User::class)->find($id);

        if (null === $user) {
            throw new NotFoundHttpException('User not found.');
        }

        return $user;
    }

    private function assertCanManageAvatar(User $user): void
    {
        $currentUser = $this->getUser();

        if ($currentUser !== $user && !$this->isGranted('ROLE_ADMIN')) {
            throw new AccessDeniedHttpException('You can only manage your own avatar.');
        }
    }

    private function deleteAvatarFile(User $user): void
    {
        if (null === $user->getAvatarFileName()) {
            return;
        }

        $filePath = $this->avatarUploadDir.'/'.$user->getAvatarFileName();

        if (file_exists($filePath)) {
            unlink($filePath);
        }
    }
}
