<?php

declare(strict_types=1);

namespace App\Framework\Http\Services;

use App\Framework\Http\IpAddress;
use InvalidArgumentException;

final class IpNetworkService
{
    public function getSubnet(IpAddress $ip, int $prefixLength): string
    {
        if ($ip->isV4()) {
            return $this->getSubnetV4($ip, $prefixLength);
        }

        throw new InvalidArgumentException('IPv6 subnet calculation not implemented yet');
    }

    public function inSameSubnet(IpAddress $ip1, IpAddress $ip2, int $prefixLength): bool
    {
        if ($ip1->isV4() && $ip2->isV4()) {
            return $this->getSubnetV4($ip1, $prefixLength) === $this->getSubnetV4($ip2, $prefixLength);
        }

        return false;
    }

    public function getLocationContext(IpAddress $ip): array
    {
        return [
            'is_local' => $ip->isLoopback(),
            'is_private' => $ip->isPrivate(),
            'is_public' => $ip->isPublic(),
            'version' => $ip->isV4() ? 'IPv4' : 'IPv6',
            'subnet_24' => $ip->isV4() ? $this->getSubnetV4($ip, 24) : null,
            'subnet_16' => $ip->isV4() ? $this->getSubnetV4($ip, 16) : null,
        ];
    }

    public function generateFingerprint(IpAddress $ip): string
    {
        // Create a fingerprint based on network context
        if ($ip->isV4()) {
            // Use /24 subnet for fingerprinting to allow some IP changes
            $subnet = $this->getSubnetV4($ip, 24);

            return hash('sha256', $subnet);
        }

        return hash('sha256', $ip->value);
    }

    private function getSubnetV4(IpAddress $ip, int $prefixLength): string
    {
        $ipLong = ip2long($ip->value);
        if ($ipLong === false) {
            throw new InvalidArgumentException("Invalid IPv4 address: {$ip->value}");
        }

        $mask = -1 << (32 - $prefixLength);
        $networkLong = $ipLong & $mask;

        return long2ip($networkLong) . "/$prefixLength";
    }
}
