<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\MainBundle\Doctrine\Model;

class Point implements \JsonSerializable
{
    public static string $SRID = '4326';

    private function __construct(private readonly ?float $lon, private readonly ?float $lat) {}

    public static function fromArrayGeoJson(array $array): self
    {
        if ('Point' === $array['type'] && isset($array['coordinates'])) {
            return self::fromLonLat($array['coordinates'][0], $array['coordinates'][1]);
        }

        throw new \Exception('Unable to build a point from input data.');
    }

    public static function fromGeoJson(string $geojson): self
    {
        $a = json_decode($geojson, null, 512, JSON_THROW_ON_ERROR);

        if (null === $a) {
            throw PointException::badJsonString($geojson);
        }

        if (null === $a->type || null === $a->coordinates) {
            throw PointException::badJsonString($geojson);
        }

        if ('Point' !== $a->type) {
            throw PointException::badGeoType();
        }

        [$lon, $lat] = $a->coordinates;

        return Point::fromLonLat($lon, $lat);
    }

    public static function fromLonLat(float $lon, float $lat): self
    {
        if ((-180 < $lon && 180 > $lon) && (-90 < $lat && 90 > $lat)) {
            return new Point($lon, $lat);
        }

        throw PointException::badCoordinates($lon, $lat);
    }

    public function getLat(): float
    {
        return $this->lat;
    }

    public function getLon(): float
    {
        return $this->lon;
    }

    public function jsonSerialize(): array
    {
        return $this->toArrayGeoJson();
    }

    public function toArrayGeoJson(): array
    {
        return [
            'type' => 'Point',
            'coordinates' => [$this->lon, $this->lat],
        ];
    }

    public function toGeoJson(): string
    {
        $array = $this->toArrayGeoJson();

        return \json_encode($array, JSON_THROW_ON_ERROR);
    }

    public function toWKT(): string
    {
        return sprintf('SRID=%s;POINT(%s %s)', self::$SRID, $this->lon, $this->lat);
    }
}
