<?php

namespace App\Controller;

use App\Entity\Stats;
use App\Entity\Place;
use App\Service\Motocultrice;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use GuzzleHttp\Client;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mailer\MailerInterface;
use App\Service\ActionLogger;

class PublicController extends AbstractController
{

    private $hide_filled_inputs = true;

    public function __construct(
        private EntityManagerInterface $entityManager,
        private Motocultrice $motocultrice,
        private MailerInterface $mailer,
        private ActionLogger $actionLogger
    ) {}

    #[Route('/propose-email/{email}/{type}/{id}', name: 'app_public_propose_email')]
    public function proposeEmail(string $email, string $type, int $id): Response
    {


        $data = $this->motocultrice->get_osm_object_data($type, $id);
        // Récupérer le code postal depuis les tags, sinon mettre -1
        $zipCode = isset($data['tags_converted']['addr:postcode']) ? (int)$data['tags_converted']['addr:postcode'] : -1;
        $place_name = $data['tags_converted']['name'];

        // Vérifier si une Place existe déjà avec le même osm_kind et osmId
        $existingPlace = $this->entityManager->getRepository(Place::class)->findOneBy([
            'osm_kind' => $type,
            'osmId' => $id
        ]);

        if ($existingPlace) {
            // Mettre à jour l'email de la Place existante
            $existingPlace->setEmail($email)->setLastContactAttemptDate(new \DateTime());
            if ($zipCode != -1) {
                $existingPlace->setZipCode($zipCode);
            }
            $this->entityManager->persist($existingPlace);
            $this->entityManager->flush();

            $debug = '';
            if ($this->getParameter('kernel.environment') !== 'prod') {
                $debug = 'Bonjour, nous sommes des bénévoles d\'OpenStreetMap France et nous vous proposons de modifier les informations de votre commerce. Voici votre lien unique de modification: ' . $this->generateUrl('app_public_edit', [
                    'zipcode' => $zipCode,
                    'name' => $place_name,
                    'uuid' => $existingPlace->getUuidForUrl()
                ], true);
            }

            $this->addFlash('success', 'L\'email a été mis à jour. Un email vous sera envoyé avec le lien de modification. ' . $debug);
        } else {


            // Créer une nouvelle entité Place
            $place = new Place();
            $place->setEmail($email)
                ->setOsmId($id)
                ->setOsmKind($type)
                ->setAskedHumainsSupport(false)
                ->setOptedOut(false)
                ->setDead(false)
                ->setNote('')
                ->setModifiedDate(new \DateTime())
                ->setZipCode($zipCode)
                ->setPlaceCount(0)
                ->setMainTag($this->motocultrice->find_main_tag($data['tags_converted']) ?? '')
                ->setStreet($this->motocultrice->find_street($data['tags_converted']) ?? '')
                ->setHousenumber($this->motocultrice->find_housenumber($data['tags_converted']) ?? '')
                ->setLastContactAttemptDate(new \DateTime())
                ->setUuidForUrl(uniqid());

            $this->entityManager->persist($place);
            $this->entityManager->flush();

            $debug = '';
            if ($this->getParameter('kernel.environment') !== 'prod') {
                $debug = 'Bonjour, nous sommes des bénévoles d\'OpenStreetMap France et nous vous proposons de modifier les informations de votre commerce. Voici votre lien unique de modification: ' . $this->generateUrl('app_public_edit', [
                    'zipcode' => $zipCode,
                    'name' => $place_name,
                    'uuid' => $place->getUuidForUrl()
                ], true);
            }
            $this->addFlash('success', 'Un email vous sera envoyé avec le lien de modification. ' . $debug);
        }

        // Envoyer l'email
        $destinataire = $this->getParameter('kernel.environment') === 'prod' ? $email : 'contact+essai_osm_commerce@cipherbliss.com';

        $message = (new Email())
            ->from('contact@osm-commerce.fr')
            ->to($destinataire)
            ->subject('Votre lien de modification OpenStreetMap')
            ->text('Bonjour, nous sommes des bénévoles d\'OpenStreetMap France et nous vous proposons de modifier les informations de votre commerce. Voici votre lien unique de modification: ' . $this->generateUrl('app_public_edit', [
                'zipcode' => $zipCode,
                'name' => $place_name,
                'uuid' => $existingPlace ? $existingPlace->getUuidForUrl() : $place->getUuidForUrl()
            ], true));

        $this->mailer->send($message);

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

    #[Route('/', name: 'app_public_index')]
    public function index(): Response
    {
        $stats = $this->entityManager->getRepository(Stats::class)->findAll();

        // Préparer les données pour la carte
        $citiesForMap = [];
        
        foreach ($stats as $stat) {
            if ($stat->getZone() && $stat->getZone() !== 'undefined' && preg_match('/^\d+$/', $stat->getZone()) && $stat->getZone() !== '00000') {
                $cityName = $stat->getName() ?: $stat->getZone();
                
                // Utiliser les coordonnées stockées si disponibles
                if ($stat->getLat() && $stat->getLon()) {
                    $citiesForMap[] = [
                        'name' => $cityName,
                        'zone' => $stat->getZone(),
                        'coordinates' => [
                            'lat' => (float) $stat->getLat(),
                            'lon' => (float) $stat->getLon()
                        ],
                        'placesCount' => $stat->getPlacesCount(),
                        'completionPercent' => $stat->getCompletionPercent(),
                        'population' => $stat->getPopulation(),
                        'url' => $this->generateUrl('app_admin_stats', ['insee_code' => $stat->getZone()])
                    ];
                }
            }
        }

        return $this->render('public/home.html.twig', [
            'controller_name' => 'PublicController',
            'stats' => $stats,
            'citiesForMap' => $citiesForMap,
            'maptiler_token' => $_ENV['MAPTILER_TOKEN'] ?? null
        ]);
    }

    /**
     * Récupère les coordonnées d'une ville via l'API Nominatim
     */
    private function getCityCoordinates(string $cityName, string $inseeCode): ?array
    {
        // Cache simple pour éviter trop d'appels API
        $cacheKey = 'city_coords_' . $inseeCode;
        
        // Vérifier le cache (ici on utilise une approche simple)
        // En production, vous pourriez utiliser le cache Symfony
        
        $query = urlencode($cityName . ', France');
        $url = "https://nominatim.openstreetmap.org/search?q={$query}&format=json&limit=1&countrycodes=fr";
        
        try {
            // Ajouter un délai pour respecter les limites de l'API Nominatim
            usleep(100000); // 0.1 seconde entre les appels
            
            $context = stream_context_create([
                'http' => [
                    'timeout' => 5, // Timeout de 5 secondes
                    'user_agent' => 'OSM-Commerces/1.0'
                ]
            ]);
            
            $response = file_get_contents($url, false, $context);
            
            if ($response === false) {
                error_log("DEBUG: Échec de récupération des coordonnées pour $cityName ($inseeCode)");
                return null;
            }
            
            $data = json_decode($response, true);
            
            if (!empty($data) && isset($data[0]['lat']) && isset($data[0]['lon'])) {
                error_log("DEBUG: Coordonnées trouvées pour $cityName ($inseeCode): " . $data[0]['lat'] . ", " . $data[0]['lon']);
                return [
                    'lat' => (float) $data[0]['lat'],
                    'lon' => (float) $data[0]['lon']
                ];
            } else {
                error_log("DEBUG: Aucune coordonnée trouvée pour $cityName ($inseeCode)");
            }
        } catch (\Exception $e) {
            error_log("DEBUG: Exception lors de la récupération des coordonnées pour $cityName ($inseeCode): " . $e->getMessage());
        }
        
        return null;
    }

    #[Route('/edit/{zipcode}/{name}/{uuid}', name: 'app_public_edit')]
    public function edit_with_uuid($zipcode, $name, $uuid): Response
    {
        $this->actionLogger->log('edit_place', [
            'zipcode' => $zipcode,
            'name' => $name,
            'uuid' => $uuid,

        ]);

        $place = $this->entityManager->getRepository(Place::class)->findOneBy(['uuid_for_url' => $uuid]);
        if (!$place) {
            $this->addFlash('warning', 'Ce lien de modification n\'existe pas.' . $uuid);
            return $this->redirectToRoute('app_public_index');
        }

        if ($place->getOsmKind() === 'relation') {
            $this->addFlash('warning', 'Les objets OSM de type "relation" ne sont pas gérés dans cet outil.');
            return $this->redirectToRoute('app_public_index');
        }

        // récupérer les tags de base
        $base_tags = $this->motocultrice->base_tags;
        $base_tags = array_fill_keys($base_tags, '');

        $commerce_overpass = $this->motocultrice->get_osm_object_data($place->getOsmKind(), $place->getOsmId());
        // Mettre à jour la Place à partir des infos Overpass
        $place->update_place_from_overpass_data($commerce_overpass);
        $this->entityManager->persist($place);
        $this->entityManager->flush();
        // Fusionner les tags de base avec les tags existants
        $commerce_overpass['tags_converted'] = array_merge($base_tags, $commerce_overpass['tags_converted']);

        // Trier les tags par ordre alphabétique des clés
        ksort($commerce_overpass['tags_converted']);

        $place->setDisplayedDate(new \DateTime());
        $this->entityManager->persist($place);
        $this->entityManager->flush();

        return $this->render('public/edit.html.twig', [
            'commerce_overpass' => $commerce_overpass,
            'name' => $name,
            'commerce' => $place,
            'zone' => $zipcode,
            'completion_percentage' => $place->getCompletionPercentage(),
            'hide_filled_inputs' => $this->hide_filled_inputs,
            'excluded_tags_to_render' => $this->motocultrice->excluded_tags_to_render,
            'osm_kind' => $place->getOsmKind(),
            "mapbox_token" => $_ENV['MAPBOX_TOKEN'],
            "maptiler_token" => $_ENV['MAPTILER_TOKEN'],
        ]);
    }

    #[Route('/dashboard', name: 'app_public_dashboard')]
    public function dashboard(): Response
    {

        $this->actionLogger->log('dashboard', []);
                        
        $stats_repo = $this->entityManager->getRepository(Stats::class)->findAll();

        $stats_for_chart = [];
        foreach ($stats_repo as $stat) {
            if ($stat->getPlacesCount() > 0 && $stat->getName() !== null && $stat->getPopulation() > 0) {
                $stats_for_chart[] = [
                    'name' => $stat->getName(),
                    'placesCount' => $stat->getPlacesCount(),
                    'completionPercent' => $stat->getCompletionPercent(),
                    'population' => $stat->getPopulation(),
                    'zone' => $stat->getZone(),
                    'osmDataDateAvg' => $stat->getOsmDataDateAvg() ? $stat->getOsmDataDateAvg()->format('Y-m-d') : null,
                ];
            }
        }

        // Compter le nombre total de lieux
        $placesCount = $this->entityManager->getRepository(Place::class)->count([]);
        return $this->render('public/dashboard.html.twig', [
            'controller_name' => 'PublicController',
            'mapbox_token' => $_ENV['MAPBOX_TOKEN'] ?? null,
            'maptiler_token' => $_ENV['MAPTILER_TOKEN'] ?? null,
            'stats' => json_encode($stats_for_chart),
            'stats_list' => $stats_repo,
            'places_count' => $placesCount,
        ]);
    }

    #[Route('/modify/{osm_object_id}/{version}/{changesetID}', name: 'app_public_submit')]
    public function submit($osm_object_id, $version, $changesetID): Response
    {

        $this->actionLogger->log('submit_object', [
            'osm_id' => $osm_object_id,
            'version' => $version,
            'changesetID' => $changesetID
        ]);
        $place = $this->entityManager->getRepository(Place::class)->findOneBy(['osmId' => $osm_object_id]);
        if (!$place) {
            $this->addFlash('warning', 'Ce commerce n\'existe pas.');
            return $this->redirectToRoute('app_public_index');
        }

        // Récupérer les données POST
        $request = Request::createFromGlobals();

        $status = null;
        $exception = false;
        $exception_message = null;
        $osm_kind = 'node';
        // Vérifier si des données ont été soumises
        if ($request->isMethod('POST')) {
            $status = "non modifié";
            $osm_kind = $request->request->get('osm_kind', 'node');
            // Récupérer tous les tags du formulaire
            $tags = [];
            $request_post = $request->request->all(); 
            $request_post = $this->motocultrice->map_post_values($request_post);
            $request_post = $request_post ?? [];
            // Log temporaire pour debug POST
            file_put_contents('/tmp/debug_post.txt', print_r($request_post, true));
            // Debug visuel immédiat
            $excluded_post_fields = [];
            foreach ($request_post as $key => $value) {
                if (strpos($key, 'commerce_tag_value__') === 0) {
                    $tagKey = str_replace('commerce_tag_value__', '', $key);
                    // On ajoute la clé même si la valeur est vide (pour affichage suppression)
                    $tags[$tagKey] = trim($value);
                } else {
                    $excluded_post_fields[] = $key;
                }
            }
            // Récupérer les tags Overpass avant modification
            $currentObjectData = $this->motocultrice->get_osm_object_data($osm_kind, $osm_object_id);
            $tags_before_modif = $currentObjectData['tags_converted'] ?? [];
            $tags_after_modif = $tags;
            // var_dump('POST brut', $_POST);
            // var_dump('request->request->all()', $request->request->all());
            // var_dump('tags extraits', $tags);
            // DEBUG : enlever ce die après analyse
            // die('DEBUG avant envoi OSM');

            // Récupérer le token OSM depuis les variables d'environnement
            $osm_api_token = $_ENV['APP_OSM_BEARER'];

            try {
                $client = new Client();

                // 1. Créer un nouveau changeset
                $changesetXml = new \SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><osm version="0.6"></osm>');
                $changeset = $changesetXml->addChild('changeset');
                $tag = $changeset->addChild('tag');
                $tag->addAttribute('k', 'created_by');
                $tag->addAttribute('v', 'OSM Mon Commerce Web Editor');

                $tag = $changeset->addChild('tag');
                $tag->addAttribute('k', 'comment');
                $tag->addAttribute('v', 'Modification dans #MonCommerceOSM');

                $changesetResponse = $client->put('https://api.openstreetmap.org/api/0.6/changeset/create', [
                    'body' => $changesetXml->asXML(),
                    'headers' => [
                        'Authorization' => 'Bearer ' . $osm_api_token,
                        'Content-Type' => 'application/xml'
                    ]
                ]);

                $newChangesetId = $changesetResponse->getBody()->getContents();

                // 2. Modifier l'objet avec le nouveau changeset
                $xml = new \SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><osm version="0.6"></osm>');
                $object = $xml->addChild($osm_kind);
                $object->addAttribute('id', $osm_object_id);
                $object->addAttribute('version', $version);
                $object->addAttribute('changeset', $newChangesetId);

                // Ajouter les coordonnées pour les nodes
                if ($osm_kind === 'node') {
                    if (!isset($currentObjectData['@attributes']['lat']) || !isset($currentObjectData['@attributes']['lon'])) {
                        throw new \Exception("Impossible de récupérer les coordonnées du nœud");
                    }
                    $object->addAttribute('lat', $currentObjectData['@attributes']['lat']);
                    $object->addAttribute('lon', $currentObjectData['@attributes']['lon']);
                }

                // Ajouter les tags
                foreach ($tags as $key => $value) {
                    if (!empty($key) && !empty($value)) {
                        $tag = $object->addChild('tag');
                        $tag->addAttribute('k', htmlspecialchars($key, ENT_XML1));
                        $tag->addAttribute('v', htmlspecialchars($value, ENT_XML1));
                    }
                }

                // Debug du XML généré
                $xmlString = $xml->asXML();

                $response = $client->put("https://api.openstreetmap.org/api/0.6/{$osm_kind}/" . $osm_object_id, [
                    'body' => $xmlString,
                    'headers' => [
                        'Authorization' => 'Bearer ' . $osm_api_token,
                        'Content-Type' => 'application/xml'
                    ]
                ]);

                // 3. Fermer le changeset
                $client->put('https://api.openstreetmap.org/api/0.6/changeset/' . $newChangesetId . '/close', [
                    'headers' => [
                        'Authorization' => 'Bearer ' . $osm_api_token
                    ]
                ]);

                if ($response->getStatusCode() === 200) {
                    $status = "Les tags ont été mis à jour avec succès";
                } else {
                    $status = "Erreur lors de la mise à jour des tags";
                    $this->actionLogger->log('ERROR_submit_object', [
                        'osm_id' => $osm_object_id,
                        'version' => $version,
                        'changesetID' => $changesetID,
                        'body_sent' => $xmlString,
                        'response' => $response->getBody()->getContents(),
                    ]);
                }
            } catch (\Exception $e) {
                $status = "Erreur lors de la communication avec l'API OSM: " . $e->getMessage();
                $exception = true;
                $exception_message = $e->getMessage();

                // On ne suppose plus la présence de getResponse (évite l'erreur linter)
                $this->actionLogger->log('ERROR_submit_object_exception', [
                    'osm_id' => $osm_object_id ?? null,
                    'version' => $version ?? null,
                    'changesetID' => $changesetID ?? null,
                    'body_sent' => $xmlString ?? null,
                    'exception_message' => $e->getMessage(),
                    'trace' => $e->getTraceAsString(),
                ]);
            }
        }

        // après envoi on récupère les données
        $commerce_overpass = $this->motocultrice->get_osm_object_data($osm_kind, $osm_object_id);

        $place->update_place_from_overpass_data($commerce_overpass);
        $this->entityManager->persist($place);
        $this->entityManager->flush();
        $this->entityManager->clear();

        $stats = $place->getStats();
        if (!$stats) {
            $stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zip_code' => $place->getZipCode()]);
        }
        if (!$stats) {
            $stats = new Stats();
            $stats->setZone($place->getZipCode());
        }

        $stats->addPlace($place);
        $place->setStats($stats);
        $place->setModifiedDate(new \DateTime());

        $stats->computeCompletionPercent();
        $this->entityManager->persist($stats);
        $this->entityManager->persist($place);
        $this->entityManager->flush();
        $this->entityManager->clear();


        return $this->render('public/view.html.twig', [
            'controller_name' => 'PublicController',
            'commerce' => $commerce_overpass,
            'commerce_overpass' => $commerce_overpass,
            'place' => $place,
            'status' => $status,
            'exception' => $exception,
            'exception_message' => $exception_message,
            'mapbox_token' => $_ENV['MAPBOX_TOKEN'],
            'maptiler_token' => $_ENV['MAPTILER_TOKEN'],
            'hide_filled_inputs' => false,
            'excluded_tags_to_render' => $this->motocultrice->excluded_tags_to_render,
            'tags_before_modif' => $tags_before_modif ?? null,
            'tags_after_modif' => $tags_after_modif ?? null,
            'excluded_post_fields' => $excluded_post_fields ?? null,
        ]);
    }

    #[Route('/request_email_to_modify/{osm_object_id}', name: 'app_public_request_email')]
    public function request_email($osm_object_id, Request $request): Response
    {
        $this->actionLogger->log('request_email_to_modify', [
            'osm_id' => $osm_object_id,
        ]);

        if ($request->isMethod('POST')) {
            $email = $request->request->get('email');

            try {
                // TODO: Implémenter l'envoi réel du mail

                $this->addFlash(
                    'success',
                    'Un email vous a été envoyé avec les instructions pour modifier ce lieu.'
                );
            } catch (\Exception $e) {
                $this->actionLogger->log('ERROR_request_email_to_modify', [
                    'osm_id' => $osm_object_id,
                    'exception_message' => $e->getMessage(),
                ]);
                $this->addFlash(
                    'error',
                    'Une erreur est survenue lors de l\'envoi de l\'email. Veuillez réessayer plus tard.'
                );
            }

            return $this->redirectToRoute('app_public_index');
        }
        // TODO envoyer un email
        return $this->render('public/request_email.html.twig', [
            'controller_name' => 'PublicController',
            'commerce_id' => $osm_object_id,
        ]);
    }

    #route pour signaler que le commerce est fermé
    #[Route('/closed_commerce/{osm_object_id}', name: 'app_public_closed_commerce')]
    public function closed_commerce($osm_object_id): Response
    {
        $this->actionLogger->log('closed_commerce', [
            'osm_id' => $osm_object_id,
        ]);
        $place = $this->entityManager->getRepository(Place::class)->findOneBy(['osm_id' => $osm_object_id]);
        if (!$place) {
            $this->addFlash('warning', 'Ce commerce n\'existe pas.');
            return $this->redirectToRoute('app_public_index');
        }

        $place->setClosed(true);
        $place->setDead(true);
        $this->entityManager->flush();

        return $this->render('public/closed_commerce.html.twig', [
            'controller_name' => 'PublicController',
        ]);
    }

    #[Route('/closed_commerces', name: 'app_public_closed_commerces')]
    public function closedCommerces(): Response
    {
        // Récupérer tous les commerces marqués comme fermés
        $closedPlaces = $this->entityManager->getRepository(Place::class)->findBy(['dead' => true]);

        return $this->render('public/closed_commerces.html.twig', [
            'controller_name' => 'PublicController',
            'closed_places' => $closedPlaces
        ]);
    }

    #[Route('/places_with_note', name: 'app_public_places_with_note')]
    public function places_with_note(): Response
    {
        // Récupérer tous les commerces ayant une note
        $places = $this->entityManager->getRepository(Place::class)->findBy(['has_note' => true]);
        return $this->render('public/places_with_note.html.twig', [
            'controller_name' => 'PublicController',
            'places' => $places
        ]);
    }

    #[Route('/latest_changes', name: 'app_public_latest_changes')]
    public function latestChanges(): Response
    {
        // Récupérer les commerces modifiés, triés par date de modification décroissante
        $places_modified = $this->entityManager->getRepository(Place::class)
            ->createQueryBuilder('p')
            ->where('p.modified_date IS NOT NULL')
            ->orderBy('p.modified_date', 'DESC')
            ->setMaxResults(20)
            ->getQuery()
            ->getResult();

        // Récupérer les commerces modifiés, triés par date de modification décroissante
        $places_displayed = $this->entityManager->getRepository(Place::class)
            ->createQueryBuilder('p')
            ->where('p.displayed_date IS NOT NULL')
            ->orderBy('p.displayed_date', 'DESC')
            ->setMaxResults(20)
            ->getQuery()
            ->getResult();

        return $this->render('public/latest_changes.html.twig', [
            'places_modified' => $places_modified,
            'places_displayed' => $places_displayed
        ]);
    }

    #[Route('/set_opted_out_place/{uuid}', name: 'app_public_set_opted_out_place')]
    public function set_opted_out_place($uuid)
    {
        $place = $this->entityManager->getRepository(Place::class)->findOneBy(['uuid_for_url' => $uuid]);
        $this->actionLogger->log('set_place_opted_out', [
            'uuid' => $uuid,
        ]);

        if (!$place) {
            $this->addFlash('warning', 'Ce commerce n\'existe pas.');
            return $this->redirectToRoute('app_public_index');
        }

        $place->setOptedOut(true);

    }

    #[Route('/ask-for-help', name: 'app_public_ask_for_help')]
    public function askForHelp(Request $request): Response
    {

        $this->actionLogger->log('ask_for_help', []);
        return $this->redirect('https://www.openstreetmap.fr/contact');
    }

    #[Route('/logs/actions', name: 'app_public_action_logs')]
    public function listActionLogs(): Response
    {
        $logs = $this->actionLogger->getLastLogs(100);
        return $this->render('public/action_logs.html.twig', [
            'logs' => $logs
        ]);
    }

    #[Route('/add-city', name: 'app_public_add_city')]
    public function addCity(): Response
    {
        return $this->render('public/add_city.html.twig', [
            'controller_name' => 'PublicController',
        ]);
    }

    #[Route('/stats/{insee_code}/followup-graph/{theme}', name: 'app_public_followup_graph', requirements: ['insee_code' => '\\d+', 'theme' => '[a-zA-Z0-9_]+'])]
    public function publicFollowupGraph(string $insee_code, string $theme): Response
    {
        $stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $insee_code]);
        if (!$stats) {
            $this->addFlash('error', 'Aucune stats trouvée pour ce code INSEE.');
            return $this->redirectToRoute('app_public_index');
        }

        $themes = \App\Service\FollowUpService::getFollowUpThemes();
        if (!isset($themes[$theme])) {
            $this->addFlash('error', 'Thème non reconnu.');
            return $this->redirectToRoute('app_public_index');
        }

        // Récupérer toutes les données de followup pour ce thème
        $followups = $stats->getCityFollowUps();
        $countData = [];
        $completionData = [];
        
        foreach ($followups as $fu) {
            if ($fu->getName() === $theme . '_count') {
                $countData[] = [
                    'date' => $fu->getDate()->format('Y-m-d'),
                    'value' => $fu->getMeasure()
                ];
            }
            if ($fu->getName() === $theme . '_completion') {
                $completionData[] = [
                    'date' => $fu->getDate()->format('Y-m-d'),
                    'value' => $fu->getMeasure()
                ];
            }
        }

        // Trier par date
        usort($countData, fn($a, $b) => $a['date'] <=> $b['date']);
        usort($completionData, fn($a, $b) => $a['date'] <=> $b['date']);

        return $this->render('public/followup_graph.html.twig', [
            'stats' => $stats,
            'theme' => $theme,
            'theme_label' => $themes[$theme],
            'count_data' => json_encode($countData),
            'completion_data' => json_encode($completionData),
            'icons' => \App\Service\FollowUpService::getFollowUpIcons(),
        ]);
    }

    #[Route('/edit-by-osm/{osm_kind}/{osm_id}', name: 'app_public_edit_by_osm')]
    public function editByOsm(string $osm_kind, string $osm_id, Request $request): Response
    {
        $place = $this->entityManager->getRepository(\App\Entity\Place::class)->findOneBy([
            'osm_kind' => $osm_kind,
            'osmId' => $osm_id
        ]);
        if ($place) {
            return $this->redirectToRoute('app_public_edit', [
                'zipcode' => $place->getZipCode(),
                'name' => $place->getName() !== '' ? $place->getName() : '?',
                'uuid' => $place->getUuidForUrl()
            ]);
        } else {
            $this->addFlash('warning', "Aucun lieu trouvé pour {$osm_kind} {$osm_id}.");
            $referer = $request->headers->get('referer');
            if ($referer) {
                return $this->redirect($referer);
            } else {
                return $this->redirectToRoute('app_public_index');
            }
        }
    }

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

    #[Route('/ville/{cityId}/rue/{streetName}', name: 'app_public_street')]
    public function streetView(string $cityId, string $streetName): Response
    {
        $cityStats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $cityId]);
        if (!$cityStats) {
            throw $this->createNotFoundException('Ville non trouvée');
        }
        // Décodage du nom de la rue depuis l'URL
        $streetName = urldecode($streetName);
        // On récupère tous les lieux dont le stats.zone correspond à cityId et la rue correspondante
        $places = $this->entityManager->getRepository(Place::class)->createQueryBuilder('p')
            ->leftJoin('p.stats', 's')
            ->where('s.zone = :cityId')
            ->andWhere('p.street = :streetName')
            ->setParameter('cityId', $cityId)
            ->setParameter('streetName', $streetName)
            ->getQuery()
            ->getResult();
        // Conversion des entités Place en tableau associatif pour le JS
        $placesArray = array_map(fn($place) => $place->toArray(), $places);

        // Préparer la répartition de complétion pour le graphe
        $completionBuckets = [
            '0-20%' => 0,
            '20-40%' => 0,
            '40-60%' => 0,
            '60-80%' => 0,
            '80-100%' => 0
        ];
        foreach ($places as $place) {
            $c = $place->getCompletionPercentage();
            if ($c < 20) $completionBuckets['0-20%']++;
            elseif ($c < 40) $completionBuckets['20-40%']++;
            elseif ($c < 60) $completionBuckets['40-60%']++;
            elseif ($c < 80) $completionBuckets['60-80%']++;
            else $completionBuckets['80-100%']++;
        }

        return $this->render('public/street.html.twig', [
            'city' => $cityStats->getName(),
            'city_id' => $cityId,
            'street' => $streetName,
            'places' => $places, // objets Place pour le HTML
            'places_js' => $placesArray, // pour le JS si besoin
            'completion_buckets' => $completionBuckets,
            'completion_buckets_values' => array_values($completionBuckets),
            'stats_url' => $this->generateUrl('app_admin_stats', ['insee_code' => $cityId]),
            'maptiler_token' => $_ENV['MAPTILER_TOKEN'] ?? null
        ]);
    }

    #[Route('/stats/{insee_code}/evolutions', name: 'app_public_stats_evolutions')]
    public function statsEvolutions(string $insee_code): Response
    {
        $stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $insee_code]);
        if (!$stats) {
            throw $this->createNotFoundException('Ville non trouvée');
        }
        $now = new \DateTime();
        $periods = [
            '7j' => (clone $now)->modify('-7 days'),
            '30j' => (clone $now)->modify('-30 days'),
            '6mois' => (clone $now)->modify('-6 months'),
        ];
        $followups = $stats->getCityFollowUps();
        $types = [];
        foreach ($followups as $fu) {
            $name = $fu->getName();
            if (str_ends_with($name, '_count')) {
                $type = substr($name, 0, -6);
                $types[$type][] = $fu;
            }
        }
        $evolutions = [];
        foreach ($types as $type => $fus) {
            usort($fus, fn($a, $b) => $a->getDate() <=> $b->getDate());
            $latest = end($fus);
            $evolutions[$type] = [
                'now' => $latest ? $latest->getMeasure() : null
            ];
            foreach ($periods as $label => $date) {
                $past = null;
                foreach ($fus as $fu) {
                    if ($fu->getDate() >= $date) {
                        $past = $fu->getMeasure();
                        break;
                    }
                }
                $evolutions[$type][$label] = $past !== null && $latest ? $latest->getMeasure() - $past : null;
            }
        }
        // Grouper les lieux par date de modification
        $places = $stats->getPlaces();
        $now = new \DateTime();
        $places_7j = [];
        $places_30j = [];
        $places_6mois = [];
        foreach ($places as $place) {
            $mod = $place->getModifiedDate();
            if (!$mod) continue;
            $diff = $now->diff($mod);
            $days = (int)$diff->format('%a');
            if ($days <= 7) {
                $places_7j[] = $place;
            } elseif ($days <= 30) {
                $places_30j[] = $place;
            } elseif ($days <= 180) {
                $places_6mois[] = $place;
            }
        }
        // Tri décroissant par date
        usort($places_7j, fn($a, $b) => $b->getModifiedDate() <=> $a->getModifiedDate());
        usort($places_30j, fn($a, $b) => $b->getModifiedDate() <=> $a->getModifiedDate());
        usort($places_6mois, fn($a, $b) => $b->getModifiedDate() <=> $a->getModifiedDate());
        return $this->render('public/stats_evolutions.html.twig', [
            'stats' => $stats,
            'evolutions' => $evolutions,
            'periods' => array_keys($periods),
            'places_7j' => $places_7j,
            'places_30j' => $places_30j,
            'places_6mois' => $places_6mois,
        ]);
    }
}
