<?php


namespace Shop\Application\GeoLocation;

use GeoIp\ValueObject\Address;

use Shop\Application\GeoLocation\Exceptions\CityNotFoundException;
use Shop\Domain\City\Contracts\CityEntityInterface;
use Shop\Infrastructure\Repositories\City\CityCriteria;
use Shop\Infrastructure\Repositories\City\CityRepository;
use Shop\Infrastructure\Repositories\Country\CountryRepository;
use Shop\Infrastructure\Repositories\Region\RegionRepository;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Session;

use GeoIp\GeoLocation as GeoLocationCore;
use GeoIp\Exceptions\AddressNotDefined;
use GeoIp\Source\SypexGeo;
use GeoIp\Concrete\Curl;

/**
 * Class GeoLocationService
 * @package Shop\Application\GeoLocation
 */
class GeoLocationService
{
    protected $request;
    protected $session;
    protected $cityRepository;
    protected $countryRepository;
    protected $regionRepository;

    /**
     * @param Request $request
     * @param Session $session
     * @param CountryRepository $countryRepository
     * @param RegionRepository $regionRepository
     * @param CityRepository $cityRepository
     */
    public function __construct(Request $request, 
                                Session $session,
                                CountryRepository $countryRepository,
                                RegionRepository $regionRepository,
                                CityRepository $cityRepository)
    {
        $this->request = $request;
        $this->session = $session;
        $this->cityRepository = $cityRepository;
        $this->countryRepository = $countryRepository;
        $this->regionRepository = $regionRepository;
    }

    /**
     * @return RegionRepository
     */
    public function getRegionRepository(): RegionRepository
    {
        return $this->regionRepository;
    }

    /**
     * @return CityRepository
     */
    public function getCityRepository(): CityRepository
    {
        return $this->cityRepository;
    }

    /**
     * @return CountryRepository
     */
    public function getCountryRepository(): CountryRepository
    {
        return $this->countryRepository;
    }


    /**
     *
     * @return \GeoIp\ValueObject\Address
     */
    public function getCurrentAddress(): Address
    {
        $ip = getenv('GEOIP') ?: $this->request->server->get('REMOTE_ADDR');

        if ($this->session->has('geoIp_$ip')) {
            return unserialize($this->session->get('geoIp_$ip'), []);
        }

        $curl = new Curl;

        //$source = new GeoIp\Source\TwoIp(new Curl);
        //$source = new GeoIp\Source\Tradesoft(new Curl);
        $source = new SypexGeo($curl);
        //$source = new \GeoIp\Source\Freegeoip(new \GeoIp\Concrete\Curl);

        $geo = new GeoLocationCore($source);

        try {
            $address = $geo->getAddressByIp($ip);
        } catch (AddressNotDefined $ex) {
            $address = new Address(
                '178.219.186.12',
                'Москва',
                'Москва',
                'Россия',
                60,
                100
            );
        }

        $this->session->set("geoIp_$ip", serialize($address));

        return $address;
    }

    /**
     * @param Address $address
     * @return CityEntityInterface
     */
    public function getCityByGeoAddress(Address $address): CityEntityInterface
    {
        $cities = $this->cityRepository->findByCriteria(
            CityCriteria::create()->setSearchByName($address->getCity())
        );

        if ($cities->count() === 0) {
            throw new CityNotFoundException('city not found by name ' . $address->getCity());
        }

        return $cities->current();
    }
}