<?php

declare(strict_types=1);

namespace App\State\Provider\Utils;

use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\ApiResources\Utils\GpsCoordinate;
use App\Service\Utils\GpsCoordinateUtils;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;

/**
 * Class GpsCoordinateSearchingProvider : custom provider pour assurer l'alimentation de la ressource GpsCoordinate.
 */
final class GpsCoordinateSearchingProvider implements ProviderInterface
{
    public function __construct(
        private readonly GpsCoordinateUtils $gpsCoordinateUtils,
        private readonly RequestStack $requestStack,
    ) {
    }

    /**
     * @param mixed[] $uriVariables
     * @param mixed[] $context
     *
     * @return GpsCoordinate[]|GpsCoordinate
     *
     * @throws ClientExceptionInterface
     * @throws RedirectionExceptionInterface
     * @throws ServerExceptionInterface
     * @throws TransportExceptionInterface
     */
    public function provide(Operation $operation, array $uriVariables = [], array $context = []): array|GpsCoordinate
    {
        if ($operation instanceof GetCollection) {
            return $this->getCollection();
        } elseif ($operation instanceof Get) {
            if (isset($uriVariables['latitude'], $uriVariables['longitude'])) {
                return $this->getReverseItem($uriVariables['latitude'], $uriVariables['longitude']);
            } else {
                throw new \InvalidArgumentException('Latitude and longitude must be provided for the Get operation.');
            }
        }

        throw new \InvalidArgumentException('Operation not supported.');
    }

    /**
     * Gérer la récupération d'une entité GpsCoordinate basée sur la latitude et la longitude.
     */
    public function getReverseItem(string $latitude, string $longitude): GpsCoordinate
    {
        try {
            $address = $this->gpsCoordinateUtils->reverseGpsCoordinates((float) $latitude, (float) $longitude);

            return $this->gpsCoordinateUtils->createGpsCoordinate($address);
        } catch (\Exception $e) {
            throw new NotFoundHttpException('Failed to retrieve GPS coordinates: '.$e->getMessage());
        }
    }

    /**
     * @return GpsCoordinate[]
     *
     * @throws ClientExceptionInterface
     * @throws RedirectionExceptionInterface
     * @throws ServerExceptionInterface
     * @throws TransportExceptionInterface
     */
    public function getCollection(): array
    {
        $request = $this->requestStack->getCurrentRequest();
        $responses = [];
        try {
            if ($request) {
                $addresses = $this->gpsCoordinateUtils->searchGpsCoordinates(
                    $request->get('street'),
                    $request->get('cp'),
                    $request->get('city')
                );
                foreach ($addresses as $address) {
                    $responses[] = $this->gpsCoordinateUtils->createGpsCoordinate($address);
                }
            }
        } catch (\Exception) {
            $responses[] = new GpsCoordinate();
        }

        return $responses;
    }

    /**
     * @throws ClientExceptionInterface
     * @throws RedirectionExceptionInterface
     * @throws ServerExceptionInterface
     * @throws TransportExceptionInterface
     */
    public function getItems(string $id): GpsCoordinate
    {
        list($latitude, $longitude) = explode(',', $id);
        try {
            $address = $this->gpsCoordinateUtils->reverseGpsCoordinates((float) $latitude, (float) $longitude);
            $gpsCoordinateResponse = $this->gpsCoordinateUtils->createGpsCoordinate($address);
        } catch (\Exception) {
            $gpsCoordinateResponse = new GpsCoordinate();
        }

        return $gpsCoordinateResponse;
    }
}
