<?php

namespace App\State;

use App\Entity\Address;
use Doctrine\DBAL\Exception;
use App\Service\ApiAddressService;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RequestStack;

class AddressProvider implements ProviderInterface
{
    private $requestStack;
    private $apiAdressService;
    private $entityManager;

    public function __construct(RequestStack $requestStack, ApiAddressService $apiAdressService, EntityManagerInterface $entityManager)
    {
        $this->requestStack = $requestStack;
        $this->apiAdressService = $apiAdressService;
        $this->entityManager = $entityManager;
    }

    public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
    {
        // Vérifie le nom de l'opération pour décider quelle méthode appeler
        if ($operation->getName() === 'fetch-municipalities') {
            return $this->fetchMunicipalities(); // Appel de la méthode pour récupérer les communes
        } elseif ($operation->getName() === 'fetch-address') {
            return $this->fetchAddress(); // Appel de la méthode pour récupérer les adresses
        } elseif ($operation->getName() === 'address-list') {
            return $this->addressList(); // Appel de la méthode pour récupérer les adresses
        } elseif ($operation->getName() === 'find-lat-long') {
            return $this->findLatLong(); // Appel de la méthode pour récupérer les adresses
        } elseif ($operation->getName() === 'fetch-municipalities-new') {
            return $this->fetchMunicipalitiesNew(); // Appel de la méthode pour récupérer les communes
        } elseif ($operation->getName() === 'fetch-address-new') {
            return $this->fetchAddressNew(); // Appel de la méthode pour récupérer les adresses
        }elseif ($operation->getName() === 'fetch-track-number') {
            return $this->fetchTrackNumber(); // Appel de la méthode pour récupérer les adresses
        } elseif ($operation->getName() === 'fetch-full-address') {
            return $this->fetchFullAddress(); // Appel de la méthode pour récupérer les adresses
        }

        return null;
    }
    public function fetchMunicipalities()
    {
        try {
            // Récupération de la requête en cours et extraction du paramètre postalCode
            $request = $this->requestStack->getCurrentRequest();
            $postalCode = $request->query->get('postalCode');

            // Vérification si le code postal est fourni
            if ($postalCode == null) {
                return new JsonResponse(
                    ['message' => 'Code postal est obligatoire !', 'data' => []],
                    Response::HTTP_OK
                );
            }

            // Récupération des communes via le service ApiAddressService
            $municipalities = $this->apiAdressService->fetchMunicipalities($postalCode);

            // Formatage des données des communes, prendre sauf le codeCity et name de la resultat retournée
            $data = [];
            foreach ($municipalities as $municipality) {
                $data[] = [
                    'codeCity' => $municipality['code'],
                    'name' => $municipality['nom']
                ];
            }

            // Génération du message de retour
            $message = count($municipalities) > 0 ? 'Communes récupérées avec succès.' : 'Aucune ville trouvée.';
            return new JsonResponse([
                'message' => $message,
                'data' => $data
            ], Response::HTTP_OK);
        } catch (\Exception $e) {
            return new JsonResponse([
                'message' => "Une erreur s\'est produite lors de la récupération des communes: %s " . $e->getMessage(),
                'data' => []
            ], Response::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    public function fetchAddress()
    {
        try {
            // Récupération des paramètres de la requête
            $request = $this->requestStack->getCurrentRequest();
            $postalCode = $request->query->get('postalCode');
            $cityCode = $request->query->get('cityCode');
            $q = $request->query->get('q');
            $limit = $request->query->get('limit');

            // Vérification des paramètres obligatoires
            if ($postalCode == null) {
                return new JsonResponse(
                    [
                        'message' => 'Postal code est obligatoire !',
                        'data' => []
                    ],
                    Response::HTTP_OK
                );
            }

            if ($cityCode == null) {
                return new JsonResponse(
                    ['message' => 'City code est obligatoire !', 'data' => []],
                    Response::HTTP_OK
                );
            }

            if ($q == null) {
                return new JsonResponse(
                    ['message' => 'q est obligatoire !', 'data' => []],
                    Response::HTTP_OK
                );
            }

            if (strlen($q) < 3 || !ctype_alnum($q[0])) {
                return new JsonResponse(
                    ['message' => 'q doit contenir entre 3 et 200 caractères et commencer par un chiffre ou une lettre !', 'data' => []],
                    Response::HTTP_OK
                );
            }

            if ($limit == null) {
                return new JsonResponse(
                    ['message' => 'Limit est obligatoire !', 'data' => []],
                    Response::HTTP_OK
                );
            }


            // Appel au service pour récupérer les adresses
            $addresses = $this->apiAdressService->fetchAddresses($q, $postalCode, $cityCode, $limit);

            $data = [];
            foreach ($addresses as $address) {
                $label = $address['properties']['label'];
                // Conversion en minuscules pour une recherche insensible à la casse
                $labelLower = strtolower($label);
                $searchLower = strtolower($q);
                if (strpos($labelLower, $searchLower) !== false) {
                    $data[] = [
                        'label' => $label,
                        'name' => $address['properties']['name'],
                        'context' => $address['properties']['context'],
                        'lat' => $address['geometry']['coordinates'][1],
                        'long' => $address['geometry']['coordinates'][0]
                    ];
                }
            }

            // Génération du message de retour
            $message = count($addresses) > 0 ? 'Adresses récupérées avec succès.' : 'Aucune adresse trouvée.';
            return new JsonResponse([
                'message' => $message,
                'data' => $data
            ], Response::HTTP_OK);
        } catch (\Exception $e) {
            // Gestion des erreurs et retour d'une réponse JSON avec un message d'erreur
            return new JsonResponse([
                'message' => "Une erreur s\'est produite lors de la récupération des adresses: %s " . $e->getMessage(),
                'data' => []
            ], Response::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    public function addressList()
    {
        try {
            $request = $this->requestStack->getCurrentRequest();
            // Récupération des paramètres de la requête
            $page = (int) $request->get('nbPage'); // Numéro de la page (par défaut : 1)
            $itemsPerPage = (int) $request->get('itemsPerPage'); // Nombre d'éléments par page (par défaut : 10)
            $orderBy = $request->get('orderBy'); // Critère de tri (par défaut : 'id')
            $direction = strtoupper($request->get('direction')); // Direction du tri (par défaut : 'ASC')
            $address = $request->get('address'); // Critère de recherche (optionnel)

            // Vérification de la validité de la direction du tri
            if ($direction && !in_array($direction, ['ASC', 'DESC'], true)) {
                return new JsonResponse(
                    ['message' => 'Direction de tri invalide (valeurs autorisées : ASC, DESC)'],
                    Response::HTTP_BAD_REQUEST
                );
            }

            $addressList = $this->entityManager->getRepository(Address::class)
                ->getPaginatedAddresses(
                    $page,           // Numéro de la page
                    $itemsPerPage,   // Nombre d'éléments par page
                    $orderBy,          // Critère de tri (optionnel)
                    $direction,          // Critère de tri (optionnel)
                    $address // Critère de recherche (optionnel)
                );

            // Retourner les informations des patients sous forme de réponse JSON
            return new JsonResponse(
                $addressList,
                Response::HTTP_OK
            );
        } catch (Exception $e) {
            // Gestion des erreurs et retour d'un message d'erreur
            return new JsonResponse(
                ['message' => 'Une erreur est survenue : ' . $e->getMessage()],
                Response::HTTP_INTERNAL_SERVER_ERROR
            );
        }
    }
    public function findLatLong()
    {
        try {
            // Récupération de la requête en cours et extraction du paramètre postalCode
            $request = $this->requestStack->getCurrentRequest();
            $codePostal = $request->get('codePostal');
            $numero = $request->get('numero');
            $nomVoie = $request->get('nomVoie');

            if (!$codePostal && !$numero && !$nomVoie) {
                return new JsonResponse(
                    ['lat' => null, 'lon' => null],
                    Response::HTTP_OK
                );
            }

            $address = ['codePostal' => $codePostal, 'numero' => $numero, 'nomVoie' => $nomVoie];
            // Récupération des coordonnées via le service ApiAddressService
            $coordinates = $this->entityManager->getRepository(Address::class)->findLatAndLong($address);
            // Génération du message de retour
            return new JsonResponse($coordinates);
        } catch (\Exception $e) {
            return new JsonResponse([
                'message' => "Une erreur s\'est produite lors de la récupération des coordonnées: %s " . $e->getMessage(),
                'data' => []
            ], Response::HTTP_INTERNAL_SERVER_ERROR);
        }
    }
    public function fetchMunicipalitiesNew()
    {
        try {
            // Récupération de la requête en cours et extraction du paramètre postalCode
            $request = $this->requestStack->getCurrentRequest();
            $postalCode = $request->query->get('postalCode');

            // Vérification si le code postal est fourni
            if ($postalCode == null) {
                return new JsonResponse(
                    ['message' => 'Code postal est obligatoire !', 'data' => []],
                    Response::HTTP_OK
                );
            }

            // Récupération des communes via le service ApiAddressService
            $municipalities = $this->entityManager->getRepository(Address::class)->fetchMunicipalities($postalCode);

            // Génération du message de retour
            $message = count($municipalities) > 0 ? 'Communes récupérées avec succès.' : 'Aucune ville trouvée.';
            return new JsonResponse([
                'message' => $message,
                'data' => $municipalities
            ], Response::HTTP_OK);
        } catch (\Exception $e) {
            return new JsonResponse([
                'message' => "Une erreur s\'est produite lors de la récupération des communes: %s " . $e->getMessage(),
                'data' => []
            ], Response::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    public function fetchAddressNew()
    {
        try {
            // Récupération des paramètres de la requête
            $request = $this->requestStack->getCurrentRequest();
            $postalCode = $request->query->get('postalCode');
            $cityCode = $request->query->get('cityCode');
            $q = $request->query->get('q');
            $trackNumber = $request->query->get('trackNumber');
            $limit = $request->query->get('limit');

            // Vérification des paramètres obligatoires
            if ($postalCode == null) {
                return new JsonResponse(
                    [
                        'message' => 'Postal code est obligatoire !',
                        'data' => []
                    ],
                    Response::HTTP_OK
                );
            }

            if ($cityCode == null) {
                return new JsonResponse(
                    ['message' => 'City code est obligatoire !', 'data' => []],
                    Response::HTTP_OK
                );
            }


            if ($limit == null) {
                return new JsonResponse(
                    ['message' => 'Limit est obligatoire !', 'data' => []],
                    Response::HTTP_OK
                );
            }

            // Appel au service pour récupérer les adresses
            $addresses = $this->entityManager->getRepository(Address::class)->fetchAddresses($q, $postalCode, $cityCode, $limit, $trackNumber);

            // Génération du message de retour
            $message = count($addresses) > 0 ? 'Adresses récupérées avec succès.' : 'Aucune adresse trouvée.';
            return new JsonResponse([
                'message' => $message,
                'data' => $addresses
            ], Response::HTTP_OK);
        } catch (\Exception $e) {
            // Gestion des erreurs et retour d'une réponse JSON avec un message d'erreur
            return new JsonResponse([
                'message' => "Une erreur s\'est produite lors de la récupération des adresses: %s " . $e->getMessage(),
                'data' => []
            ], Response::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    public function fetchTrackNumber()
    {
        try {
            // Récupération des paramètres de la requête
            $request = $this->requestStack->getCurrentRequest();
            $postalCode = $request->query->get('postalCode');
            $cityCode = $request->query->get('cityCode');
            $trackName = $request->query->get('trackName');
            $limit = $request->query->get('limit');

            // Vérification des paramètres obligatoires
            if ($postalCode == null) {
                return new JsonResponse(
                    [
                        'message' => 'Postal code est obligatoire !',
                        'data' => []
                    ],
                    Response::HTTP_OK
                );
            }

            if ($cityCode == null) {
                return new JsonResponse(
                    ['message' => 'City code est obligatoire !', 'data' => []],
                    Response::HTTP_OK
                );
            }

            if ($limit == null) {
                return new JsonResponse(
                    ['message' => 'Limit est obligatoire !', 'data' => []],
                    Response::HTTP_OK
                );
            }

            // Appel au service pour récupérer les adresses
            $trackNumber = $this->entityManager->getRepository(Address::class)->fetchTrackNumber( $postalCode, $cityCode, $limit, $trackName);

            // Génération du message de retour
            $message = count($trackNumber) > 0 ? 'Numero de rue récupérées avec succès.' : 'Aucune numero de rue trouvée.';
            return new JsonResponse([
                'message' => $message,
                'data' => $trackNumber
            ], Response::HTTP_OK);
        } catch (\Exception $e) {
            // Gestion des erreurs et retour d'une réponse JSON avec un message d'erreur
            return new JsonResponse([
                'message' => "Une erreur s\'est produite lors de la récupération des numeros du rue: %s " . $e->getMessage(),
                'data' => []
            ], Response::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    public function fetchFullAddress()
    {
        try {
            // Récupération des paramètres de la requête
            $request = $this->requestStack->getCurrentRequest();
            $address = $request->query->get('address');
            $limit = $request->query->get('limit');

            // Vérification des paramètres obligatoires
            if ($address == null) {
                return new JsonResponse(
                    [
                        'message' => 'Adresse code est obligatoire !',
                        'data' => []
                    ],
                    Response::HTTP_OK
                );
            }

            // Appel au service pour récupérer les adresses
            $addresses = $this->entityManager->getRepository(Address::class)->fetchFullAddresses($address, $limit);

            // Génération du message de retour
            $message = count($addresses) > 0 ? 'Adresses récupérées avec succès.' : 'Aucune adresse trouvée.';
            return new JsonResponse([
                'message' => $message,
                'data' => $addresses
            ], Response::HTTP_OK);
        } catch (\Exception $e) {
            // Gestion des erreurs et retour d'une réponse JSON avec un message d'erreur
            return new JsonResponse([
                'message' => "Une erreur s\'est produite lors de la récupération des adresses: %s " . $e->getMessage(),
                'data' => []
            ], Response::HTTP_INTERNAL_SERVER_ERROR);
        }
    }
}
