<?php

declare(strict_types=1);

namespace utils\connectiondata;

use utils\type\ConnectionType;

final class TcpConnectionData extends ConnectionData
{
    private string $hostName;
    private string $ipAddress;
    private int $port;
    private ConnectionType $connectionType;

    public function __construct(string $hostname)
    {
        [$hostOnly, $port] = self::parseHostname($hostname);
        $this->hostName = $hostOnly . ':' . $port;
        $this->port = $port;
        $this->ipAddress = gethostbyname($hostOnly);
        $this->connectionType = ConnectionType::TCP();
    }

    private static function parseHostname(string $hostname): array
    {
        $value = trim($hostname);
        if (stripos($value, 'tcp://') === 0) {
            $value = substr($value, 6);
        }
        if (strpos($value, '/') !== false) {
            throw new \InvalidArgumentException('TCP addresses do not support route paths.');
        }

        $colon = strrpos($value, ':');
        if ($colon === false || $colon <= 0 || $colon >= strlen($value) - 1) {
            throw new \InvalidArgumentException('TCP hostname must use host:port format.');
        }

        $hostOnly = trim(substr($value, 0, $colon));
        $port = filter_var(substr($value, $colon + 1), FILTER_VALIDATE_INT);
        if ($hostOnly === '' || $port === false || $port < 0 || $port > 65535) {
            throw new \InvalidArgumentException('TCP hostname must contain a non-empty host and port between 0 and 65535.');
        }

        return [$hostOnly, $port];
    }

    public function getConnectionType(): ConnectionType
    {
        return $this->connectionType;
    }

    public function getHostname(): string
    {
        return $this->hostName;
    }

    public function serializeConnectionData(): array
    {
        $result[] = $this->getConnectionType()->getValue();
        array_push($result, ...$this->getAddressBytes());

        return array_merge($result, $this->getPortBytes());
    }

    private function getAddressBytes(): array
    {
        $ipAddress = explode('.', $this->ipAddress);
        $ipBytes = [];
        foreach ($ipAddress as $ip) {
            $ipBytes[] = (int) $ip;
        }

        return $ipBytes;
    }

    private function getPortBytes(): array
    {
        return [
            $this->port & 0xFF,
            $this->port >> 8 & 0xFF
        ];
    }

    public function getPort(): int
    {
        return $this->port;
    }

}
