<?php
declare(strict_types=1);

namespace App\Infrastructure\GeoIp;

use PDO;
use Generator;
use RuntimeException;

final class IpRangeService
{
    private const array RIR_SOURCES = [
        'RIPE' => 'https://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-latest',      // Europa, Westasien, Teile Afrikas
        'ARIN' => 'https://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest',   // Nordamerika
        'APNIC' => 'https://ftp.apnic.net/pub/stats/apnic/delegated-apnic-latest',        // Asien-Pazifik
        'LACNIC' => 'https://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-latest',    // Lateinamerika
        'AFRINIC' => 'https://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-latest' // Afrika
    ];

    private const int BATCH_SIZE = 1000;

    public function __construct(
        private readonly PDO $database
    ) {
    }

    public function loadIpRanges(): int
    {
        $this->database->exec('DELETE FROM ip_ranges');

        $this->database->beginTransaction();
        $stmt = $this->database->prepare('INSERT INTO ip_ranges (ip_start, ip_end, country) VALUES (?, ?, ?)');

        $totalProcessed = 0;

        foreach (self::RIR_SOURCES as $rir => $url) {
            echo "Verarbeite {$rir}-Datenbank...\n";

            try {
                $processed = 0;
                foreach ($this->parseIpRanges($url) as $ipRange) {
                    $stmt->execute([$ipRange['ip_start'], $ipRange['ip_end'], $ipRange['country']]);
                    $processed++;
                    $totalProcessed++;

                    if ($totalProcessed % self::BATCH_SIZE === 0) {
                        $this->database->commit();
                        $this->database->beginTransaction();
                        echo "Verarbeitet: {$totalProcessed} Einträge insgesamt\n";
                    }
                }
                echo "{$rir}: {$processed} Einträge hinzugefügt\n";

            } catch (\Exception $e) {
                echo "Fehler bei {$rir}: " . $e->getMessage() . "\n";
                // Weiter mit nächster Datenbank
            }
        }

        $this->database->commit();
        echo "Datenbank-Aufbau abgeschlossen. Insgesamt {$totalProcessed} Einträge aus allen RIRs.\n";

        return $totalProcessed;
    }

    public function getCountryCodeForIp(string $ip): ?string
    {
        $ipLong = ip2long($ip);
        if ($ipLong === false) {
            return null;
        }

        $stmt = $this->database->prepare('SELECT country FROM ip_ranges WHERE ip_start <= ? AND ip_end >= ?');
        $stmt->execute([$ipLong, $ipLong]);
        $result = $stmt->fetch(PDO::FETCH_ASSOC);

        return $result ? $result['country'] : null;
    }

    /**
     * Generator für speicherschonende Verarbeitung der IP-Ranges
     */
    private function parseIpRanges(string $url): Generator
    {
        $context = stream_context_create([
            'http' => [
                'timeout' => 300 // 5 Minuten Timeout
            ]
        ]);

        $handle = fopen($url, 'r', false, $context);
        if (!$handle) {
            throw new RuntimeException("Konnte URL nicht öffnen: {$url}");
        }

        try {
            while (($line = fgets($handle)) !== false) {
                $line = trim($line);

                if (str_contains($line, '|ipv4|')) {
                    $parts = explode('|', $line);

                    if (count($parts) < 5) {
                        continue;
                    }

                    $country = $parts[1];
                    $ip = $parts[3];
                    $count = (int)$parts[4];

                    $ipStart = ip2long($ip);
                    if ($ipStart === false) {
                        continue;
                    }

                    $ipEnd = $ipStart + $count - 1;

                    yield [
                        'ip_start' => $ipStart,
                        'ip_end' => $ipEnd,
                        'country' => $country
                    ];
                }
            }
        } finally {
            fclose($handle);
        }
    }
}
