<?php

declare(strict_types=1);

namespace App\ValueObject;

use App\Exception\InvalidPointException;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Embeddable]
class Point
{
    #[ORM\Column(name: 'lat', type: Types::FLOAT)]
    private float $lat;

    #[ORM\Column(name: 'lng', type: Types::FLOAT)]
    private float $lng;

    public function __construct(float $lat, float $lng)
    {
        $this->assertValid($lat, $lng);

        $this->lat = $lat;
        $this->lng = $lng;
    }

    public static function fromArray(array $coordinates): self
    {
        if (! isset($coordinates['lat'], $coordinates['lng'])) {
            throw InvalidPointException::missingCoordinates();
        }

        return new self((float) $coordinates['lat'], (float) $coordinates['lng']);
    }

    public static function fromLatLng(float $lat, float $lng): self
    {
        return new self($lat, $lng);
    }

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

    public function getLng(): float
    {
        return $this->lng;
    }

    private function assertValid(float $lat, float $lng): void
    {
        if (! is_finite($lat) || ! is_finite($lng)) {
            throw InvalidPointException::invalidNumber();
        }

        if ($lat < -90 || $lat > 90) {
            throw InvalidPointException::latitudeOutOfRange($lat);
        }

        if ($lng < -180 || $lng > 180) {
            throw InvalidPointException::longitudeOutOfRange($lng);
        }
    }
}
