<?php

namespace App\Controller\Frontend;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\Routing\Attribute\Route;

use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints\Url;

use Doctrine\ORM\EntityManagerInterface;

use Symfony\Contracts\Translation\TranslatorInterface;

use Symfony\Component\Process\Process;

use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;

use Symfony\Component\Messenger\MessageBusInterface;

use Symfony\Component\Filesystem\Filesystem;

use Symfony\Component\Security\Http\Attribute\IsGranted;

use App\Entity\UserDownloader;
use App\Entity\AnonymeDownloader;

use App\Service\Frontend\DownloadService;
use App\Service\Frontend\ListmonkMailerService;

use Symfony\Component\HttpFoundation\BinaryFileResponse;

use Symfony\Component\Process\Exception\ProcessFailedException;


final class HomeController extends AbstractController
{
    private string $projectDir;

    public function __construct(
        private EntityManagerInterface $em,
        private readonly Filesystem $filesystem,
        private readonly TranslatorInterface $translator,
        private ParameterBagInterface $params,
        private DownloadService $downloadService,
        private readonly ListmonkMailerService $mailer,
    ) {
        $this->projectDir = $params->get('kernel.project_dir');
    }

    #[Route('/', name: 'app_frontend_home')]
    public function index(): Response
    {
        return $this->render('/frontend/index.html.twig', []);
    }


    #[Route('/size-movie', name: 'app_frontend_size_movie')]
    public function sizeMovie(Request $request): Response
    {
        $url = $request->get('url');

        // 2. Validation stricte du format URL
        $validator = Validation::createValidator();
        $violations = $validator->validate($url, [
            new Url(['protocols' => ['http', 'https']])
        ]);
        if (count($violations) > 0 || empty($url)) {
            return new Response("URL invalide ou manquante.", Response::HTTP_BAD_REQUEST);
        }

        $process = new Process([
            'yt-dlp',
            '--print',
            '%(title)s|%(filesize,filesize_approx)s',
            '--', // 🛡️ Protection cruciale contre l'injection d'arguments
            $url
        ]);

        $process->run();

        if (!$process->isSuccessful()) {
            //echo $process->getCommandLine() . "<br>" . $process->getErrorOutput();
            //die;
            return new Response("error1", Response::HTTP_INTERNAL_SERVER_ERROR);
        }

        // 2. On récupère la sortie et on nettoie les espaces
        $output = trim($process->getOutput());

        // 3. On sépare le titre et la taille grâce au délimiteur '|'
        $parts = explode('|', $output);

        // On vérifie qu'on a bien reçu les deux éléments attendus
        if (count($parts) >= 2) {
            $filename = $parts[0];
            $bytes = trim($parts[1]);

            $megabytes = "Inconnu";
            if (is_numeric($bytes)) {
                // Calcul de conversion en Mo
                $megabytes = round($bytes / 1024 / 1024, 2);
            }

            // Option A : Retourner une réponse JSON (Recommandé si vous utilisez du JS côté front)
            return new JsonResponse([
                'filename' => $filename,
                'size' => $megabytes
            ]);
        }
        return new Response("error2", Response::HTTP_BAD_REQUEST);
    }


    #[Route('/download-streamed', name: 'app_streamed_download')]
    public function downloadStreamed(Request $request): StreamedResponse
    {
        $url = $request->query->get('url');
        $pathDownload = $this->projectDir . '/public/downloader/anonyme/';

        // Résoudre l'userId AVANT la closure (évite $this->getUser() dans le stream)

        $user = $this->getUser();

        if ($user) {
            $userId = $user?->getId();
            $pathDownload = $this->projectDir . '/public/downloader/user/' . $userId . '/';
            if (!is_dir($pathDownload)) {
                mkdir($pathDownload, 0777, true);
            }
        }

        // Récupérer le nom du fichier AVANT le stream
        $filenameProcess = new Process(['yt-dlp', '--get-filename', '-o', '%(id)s.%(ext)s|%(title)s', $url]);
        $filenameProcess->run();
        $output = trim($filenameProcess->getOutput());

        // 3. On sépare le titre et la taille grâce au délimiteur '|'
        $parts = explode('|', $output);
        if (count($parts) >= 2) {
            $fileName = $parts[0];
            $name = $parts[1];
        }


        


        set_time_limit(0);

        // Capturer le service AVANT la closure
        $downloadService = $this->downloadService;

        $response = new StreamedResponse(function () use ($url, $pathDownload, $fileName, $name, $user, $downloadService) {

            // ✅ CRUCIAL : détecter la déconnexion client
            ignore_user_abort(false);

            $process = new Process(['yt-dlp', '-o', $pathDownload . $fileName, '--newline', $url]);
            $process->setTimeout(3600);

            // ✅ Stocker le process pour pouvoir le tuer
            register_shutdown_function(function () use ($process) {
                if ($process->isRunning()) {
                    $process->stop(3); // SIGTERM, puis SIGKILL après 3s
                }
            });

            $process->run(function ($type, $buffer) use ($fileName, $process) {
                // ✅ Vérifier la déconnexion dans le callback de progression
                if (connection_aborted()) {
                    $process->stop(3);
                    exit();
                }

                if (Process::ERR !== $type) {
                    if (preg_match('/(\d+(?:\.\d+)?)\%/', $buffer, $matches)) {
                        $percentage = round((float)$matches[1]);

                        echo "data: " . json_encode([
                            'progress' => $percentage,
                            'filename' => $fileName,
                        ]) . "\n\n";

                        if (ob_get_level() > 0) ob_flush();
                        flush();
                    }
                }
            });

            // ✅ Vérifier que le process s'est bien terminé (pas tué/annulé)
            if (!$process->isSuccessful()) {
                echo "data: " . json_encode([
                    'error' => true,
                    'message' => 'Le téléchargement a échoué ou a été interrompu',
                ]) . "\n\n";
                if (ob_get_level() > 0) ob_flush();
                flush();
                return; // ← stop, pas de signal "done"
            }

            // Signal de fin
            echo "data: " . json_encode([
                'done' => true,
                'pathDownload' => $downloadService->downloadSaveStream($url, $fileName, $name, $pathDownload, $user),
            ]) . "\n\n";

            if (ob_get_level() > 0) ob_flush();
            flush();
        });

        $response->headers->set('Content-Type', 'text/event-stream');
        $response->headers->set('Cache-Control', 'no-cache');
        $response->headers->set('Connection', 'keep-alive');
        $response->headers->set('X-Accel-Buffering', 'no');

        return $response;
    }

    #[Route('/download-background', name: 'app_background_download')]
    public function download(MessageBusInterface $bus, Request $request): Response
    {
        $url = $request->get('url');
        $user = $this->getUser();

        $userDownloader = new UserDownloader();
        $userDownloader->setUrl($url);
        $userDownloader->setUser($user);
        $userDownloader->setDateAdd(new \DateTime());
        $userDownloader->setIsDelete(false);

        // 1. Récupérer d'abord le titre/nom du fichier que yt-dlp va générer
        // On utilise --get-filename pour simuler le nom final sans télécharger tout de suite
        $getNameProcess = new Process(['yt-dlp', '--get-filename', '-o', '%(id)s.%(ext)s|%(title)s', $url]);
        $getNameProcess->run();

        if (!$getNameProcess->isSuccessful()) {
            throw new ProcessFailedException($getNameProcess);
        }

        $output = trim($getNameProcess->getOutput());

        $parts = explode('|', $output);
        if (count($parts) >= 2) {
            $filename = $parts[0];
            $name = $parts[1];
            $userDownloader->setName($name);
            $userDownloader->setNameFile($filename);

            // On envoie le message dans la file d'attente
            $bus->dispatch($userDownloader);

            return new Response('Le téléchargement a démarré en tâche de fond !');
        }else{
            return new Response($output, Response::HTTP_BAD_REQUEST);
        }

    }

    #[Route('/history', name: 'app_frontend_history')]
    #[IsGranted('ROLE_USER')]
    public function history(): Response
    {
        return $this->render('/frontend/history.html.twig', [
            'videos' => $this->em->getRepository(UserDownloader::class)->findBy(['user' => $this->getUser(), 'isDelete' => false])
        ]);
    }



    #[Route('/download/user/{id}', name: 'app_frontend_download_user')]
    #[IsGranted('ROLE_USER')]
    public function downloadUser(UserDownloader $userDownloader): BinaryFileResponse
    {
        $id = $userDownloader->getId();
        $fileName = $userDownloader->getNameFile();
        $user = $userDownloader->getUser();

        $filePath = $this->projectDir . '/public/downloader/user/' . $user->getId() . '/' . $fileName;

        // Fichier introuvable sur le disque
        if (!$this->filesystem->exists($filePath)) {
            throw $this->createNotFoundException(
                $this->translator->trans('error_file_not_found', [], 'home')
            );
        }

        return $this->downloadService->downloadUser($id, $fileName, $filePath);
    }



    #[Route('/download/anonyme/{id}', name: 'app_frontend_download_anonyme')]
    public function downloadAnonyme(AnonymeDownloader $anonymeDownloader): BinaryFileResponse
    {
        $id = $anonymeDownloader->getId();
        $fileName = $anonymeDownloader->getNameFile();

        $filePath = $this->projectDir . '/public/downloader/anonyme/' . $fileName;

        // Fichier introuvable sur le disque
        if (!$this->filesystem->exists($filePath)) {
            throw $this->createNotFoundException(
                $this->translator->trans('error_file_not_found', [], 'home')
            );
        }

        return $this->downloadService->downloadUser($id, $fileName, $filePath);
    }


    #[Route('/delete-video/{id}', name: 'app_frontend_delete_video', methods: ['POST'])]
    #[IsGranted('ROLE_USER')]
    public function deleteVideo(UserDownloader $userDownloader, Request $request): Response
    {
        // 1. Validation du token CSRF
        if (!$this->isCsrfTokenValid('delete_video', $request->request->get('_token'))) {
            $this->addFlash('danger', $this->translator->trans('error_invalid_csrf', [], 'home'));

            return $this->redirectToRoute('app_frontend_history');
        }

        // 2. Suppression du fichier physique (si présent)
        $path = $this->projectDir . '/public/downloader/user/';
        $filePath = $path . $this->getUser()->getId() . '/' . $userDownloader->getNameFile();
        
        if ($this->filesystem->exists($filePath)) {
            $this->filesystem->remove($filePath);
        }

        // 3. Suppression en base
        $userDownloader->setIsDelete(true);
        $this->em->persist($userDownloader);
        $this->em->flush();

        // 4. Message flash de confirmation
        $this->addFlash(
            'success',
            $this->translator->trans('flash_video_deleted', ['%name%' => $userDownloader->getName()], 'home')
        );

        return $this->redirectToRoute('app_frontend_history');
    }



    #[Route('/test', name: 'app_test')]
    public function test()
    {
        die;
        $this->mailer->send(
            ['maher.tili@gmail.com'],
            7,
            ['name'=> 'Monjie',  'video'=> 'mon happy movie test', 'date'=> '19/06/2026 15:45', 'url'=> 'https://video.powerpme.com/downloder/user/4/'],
            'external',
            ['Content-Type' => 'application/json; charset=utf-8']
        );

        dd($result);
        die;
        return $this->render('/frontend/test.html.twig', []);
    }
}
