<?php

namespace App\Http\Controllers;

use App\Models\DnsZone;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Cache;

class DnsHealthCheckController extends Controller
{
    /**
     * Lista serverelor DNS publice pentru verificare propagare
     */
    protected array $dnsServers = [
        'Google' => ['8.8.8.8', '8.8.4.4'],
        'Cloudflare' => ['1.1.1.1', '1.0.0.1'],
        'OpenDNS' => ['208.67.222.222', '208.67.220.220'],
        'Quad9' => ['9.9.9.9', '149.112.112.112'],
        'Comodo' => ['8.26.56.26', '8.20.247.20'],
    ];

    /**
     * Afișează pagina de DNS Health Check
     */
    public function index()
    {
        $user = Auth::user();

        // Obține zonele utilizatorului pentru dropdown
        $zones = $user->isAdmin()
            ? DnsZone::orderBy('domain')->get(['id', 'domain'])
            : $user->dnsZones()->orderBy('domain')->get(['id', 'domain']);

        return view('dns-health-check.index', compact('zones'));
    }

    /**
     * Verifică propagarea DNS pentru un domeniu
     */
    public function check(Request $request)
    {
        try {
            $request->validate([
                'domain' => 'required|string|max:255',
                'record_type' => 'required|in:A,AAAA,MX,NS,TXT,CNAME,SOA',
                'name' => 'nullable|string|max:255',
            ]);

            $domain = $request->domain;
            $recordType = $request->record_type;
            $name = $request->name ? $request->name . '.' . $domain : $domain;

            $results = [];

            foreach ($this->dnsServers as $provider => $servers) {
                foreach ($servers as $server) {
                    try {
                        $result = $this->queryDnsServer($name, $recordType, $server);
                        $results[] = [
                            'provider' => $provider,
                            'server' => $server,
                            'status' => $result['status'],
                            'records' => $result['records'] ?? [],
                            'ttl' => $result['ttl'] ?? null,
                            'response_time' => $result['response_time'] ?? null,
                            'error' => $result['error'] ?? null,
                        ];
                    } catch (\Exception $e) {
                        $results[] = [
                            'provider' => $provider,
                            'server' => $server,
                            'status' => 'error',
                            'records' => [],
                            'ttl' => null,
                            'response_time' => null,
                            'error' => $e->getMessage(),
                        ];
                    }
                }
            }

            // Verifică consistența
            $consistency = $this->checkConsistency($results);

            // Calculează timpul estimat de propagare
            $propagation = $this->estimatePropagation($results);

            return response()->json([
                'success' => true,
                'domain' => $domain,
                'record_type' => $recordType,
                'name' => $name,
                'results' => $results,
                'consistency' => $consistency,
                'propagation' => $propagation,
                'checked_at' => now()->format('Y-m-d H:i:s'),
            ]);
        } catch (\Illuminate\Validation\ValidationException $e) {
            return response()->json([
                'success' => false,
                'error' => 'Date invalide: ' . implode(', ', $e->validator->errors()->all()),
            ], 422);
        } catch (\Exception $e) {
            \Log::error('DNS Health Check error: ' . $e->getMessage(), [
                'trace' => $e->getTraceAsString()
            ]);

            return response()->json([
                'success' => false,
                'error' => 'Eroare internă: ' . $e->getMessage(),
            ], 500);
        }
    }

    /**
     * Interogează un server DNS specific
     */
    protected function queryDnsServer(string $name, string $type, string $server): array
    {
        $startTime = microtime(true);

        try {
            // Folosim dig sau nslookup prin exec
            // Alternativ, putem folosi o librărie PHP pentru DNS
            $records = [];
            $ttl = null;

            // Încercăm cu dns_get_record pentru tipurile suportate
            if (in_array($type, ['A', 'AAAA', 'MX', 'NS', 'TXT', 'CNAME', 'SOA'])) {
                $dnsType = constant('DNS_' . $type);

                // Setăm temporar serverul DNS (nu funcționează nativ în PHP)
                // Folosim o abordare alternativă cu exec
                $output = [];
                $returnCode = 0;

                // Folosim nslookup pentru Windows sau dig pentru Linux
                if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
                    exec("nslookup -type={$type} {$name} {$server} 2>&1", $output, $returnCode);
                } else {
                    exec("dig @{$server} {$name} {$type} +short +time=5 2>&1", $output, $returnCode);
                }

                $responseTime = round((microtime(true) - $startTime) * 1000);

                if ($returnCode === 0 && !empty($output)) {
                    $records = $this->parseOutput($output, $type);
                    $ttl = $this->extractTtl($output);

                    return [
                        'status' => 'success',
                        'records' => $records,
                        'ttl' => $ttl,
                        'response_time' => $responseTime,
                    ];
                }

                return [
                    'status' => 'no_records',
                    'records' => [],
                    'response_time' => $responseTime,
                ];
            }

            return [
                'status' => 'unsupported',
                'error' => 'Tip de înregistrare nesupportat',
            ];

        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'error' => $e->getMessage(),
                'response_time' => round((microtime(true) - $startTime) * 1000),
            ];
        }
    }

    /**
     * Parsează output-ul comenzii DNS (nslookup pe Windows)
     */
    protected function parseOutput(array $output, string $type): array
    {
        $records = [];
        $inAnswer = false;

        foreach ($output as $line) {
            $line = trim($line);

            // Skip linii goale
            if (empty($line)) {
                continue;
            }

            // Detectează începutul răspunsului
            if (str_contains($line, 'Non-authoritative answer') || str_contains($line, 'Authoritative answer')) {
                $inAnswer = true;
                continue;
            }

            // Skip header-ul (Server: și Address: pentru serverul DNS)
            if (str_starts_with($line, 'Server:') || (str_starts_with($line, 'Address:') && !$inAnswer)) {
                continue;
            }

            if (!$inAnswer) {
                continue;
            }

            // Parsare în funcție de tipul de înregistrare
            switch ($type) {
                case 'A':
                case 'AAAA':
                    // Format: "Address:  IP" sau "Addresses:  IP"
                    if (str_contains($line, 'Address')) {
                        if (preg_match('/Address(?:es)?:\s*(.+)/', $line, $matches)) {
                            $ip = trim($matches[1]);
                            if (filter_var($ip, FILTER_VALIDATE_IP)) {
                                $records[] = $ip;
                            }
                        }
                    }
                    break;

                case 'NS':
                    // Format: "domain nameserver = ns1.example.com"
                    if (str_contains($line, 'nameserver')) {
                        if (preg_match('/nameserver\s*=\s*(.+)/', $line, $matches)) {
                            $records[] = trim($matches[1]);
                        }
                    }
                    break;

                case 'MX':
                    // Format: "domain MX preference = 10, mail exchanger = mail.example.com"
                    if (str_contains($line, 'mail exchanger')) {
                        if (preg_match('/mail exchanger\s*=\s*(.+)/', $line, $matches)) {
                            $mx = trim($matches[1]);
                            // Extrage și prioritatea dacă există
                            if (preg_match('/preference\s*=\s*(\d+)/', $line, $prefMatch)) {
                                $mx = $prefMatch[1] . ' ' . $mx;
                            }
                            $records[] = $mx;
                        }
                    }
                    break;

                case 'TXT':
                    // Format: "domain text = \"value\""
                    if (str_contains($line, 'text') || str_contains($line, 'TXT')) {
                        if (preg_match('/(?:text|TXT)\s*=\s*["\']?(.+?)["\']?\s*$/', $line, $matches)) {
                            $records[] = trim($matches[1], '"\'');
                        }
                    }
                    break;

                case 'CNAME':
                    // Format: "domain canonical name = target.example.com"
                    if (str_contains($line, 'canonical name')) {
                        if (preg_match('/canonical name\s*=\s*(.+)/', $line, $matches)) {
                            $records[] = trim($matches[1]);
                        }
                    }
                    break;

                case 'SOA':
                    // Format: multiple linii cu "primary name server = ...", "serial = ...", etc.
                    if (str_contains($line, 'primary name server')) {
                        if (preg_match('/primary name server\s*=\s*(.+)/', $line, $matches)) {
                            $records['primary'] = trim($matches[1]);
                        }
                    } elseif (str_contains($line, 'serial')) {
                        if (preg_match('/serial\s*=\s*(\d+)/', $line, $matches)) {
                            $records['serial'] = $matches[1];
                        }
                    } elseif (str_contains($line, 'responsible mail')) {
                        if (preg_match('/responsible mail addr\s*=\s*(.+)/', $line, $matches)) {
                            $records['admin'] = trim($matches[1]);
                        }
                    }
                    break;
            }
        }

        // Pentru SOA, formatează ca string
        if ($type === 'SOA' && !empty($records)) {
            $soaString = ($records['primary'] ?? 'N/A') . ' (serial: ' . ($records['serial'] ?? 'N/A') . ')';
            return [$soaString];
        }

        return array_values(array_unique($records));
    }

    /**
     * Extrage TTL din output
     */
    protected function extractTtl(array $output): ?int
    {
        foreach ($output as $line) {
            if (preg_match('/\s+(\d+)\s+IN\s+/', $line, $matches)) {
                return (int) $matches[1];
            }
        }
        return null;
    }

    /**
     * Verifică consistența rezultatelor
     */
    protected function checkConsistency(array $results): array
    {
        $successResults = array_filter($results, fn($r) => $r['status'] === 'success');

        if (empty($successResults)) {
            return [
                'status' => 'no_data',
                'message' => 'Nu s-au găsit înregistrări pe niciunul din servere',
                'percentage' => 0,
            ];
        }

        // Colectează toate valorile unice
        $allRecords = [];
        foreach ($successResults as $result) {
            $records = $result['records'] ?? [];
            if (is_array($records)) {
                sort($records); // sort() modifică array-ul in-place
                $allRecords[] = implode(',', $records);
            } else {
                $allRecords[] = '';
            }
        }

        $uniqueValues = array_unique($allRecords);
        $totalServers = count($results);
        $respondingServers = count($successResults);

        if (count($uniqueValues) === 1) {
            return [
                'status' => 'consistent',
                'message' => 'Toate serverele returnează aceleași valori',
                'percentage' => 100,
                'responding' => $respondingServers,
                'total' => $totalServers,
            ];
        }

        // Calculează procentul de consistență
        $mostCommon = array_count_values($allRecords);
        arsort($mostCommon);
        $maxCount = reset($mostCommon);
        $percentage = round(($maxCount / $respondingServers) * 100);

        return [
            'status' => 'inconsistent',
            'message' => 'Există diferențe între servere - propagare în curs',
            'percentage' => $percentage,
            'responding' => $respondingServers,
            'total' => $totalServers,
        ];
    }

    /**
     * Estimează timpul de propagare
     */
    protected function estimatePropagation(array $results): array
    {
        $successResults = array_filter($results, fn($r) => $r['status'] === 'success');

        if (empty($successResults)) {
            return [
                'status' => 'unknown',
                'message' => 'Nu se poate estima - lipsesc date',
            ];
        }

        // Calculează TTL mediu
        $ttls = array_filter(array_column($successResults, 'ttl'));
        $avgTtl = !empty($ttls) ? round(array_sum($ttls) / count($ttls)) : 3600;

        $respondingCount = count($successResults);
        $totalCount = count($results);
        $propagationPercent = ($respondingCount / $totalCount) * 100;

        if ($propagationPercent >= 90) {
            return [
                'status' => 'complete',
                'message' => 'Propagarea este completă',
                'estimated_time' => 0,
            ];
        }

        // Estimare bazată pe TTL
        $remainingPercent = 100 - $propagationPercent;
        $estimatedSeconds = ($remainingPercent / 100) * $avgTtl;

        return [
            'status' => 'in_progress',
            'message' => 'Propagarea este în curs',
            'estimated_time' => $estimatedSeconds,
            'estimated_formatted' => $this->formatTime($estimatedSeconds),
            'current_percentage' => round($propagationPercent),
        ];
    }

    /**
     * Formatează timpul în format lizibil
     */
    protected function formatTime(int $seconds): string
    {
        if ($seconds < 60) {
            return "{$seconds} secunde";
        }
        if ($seconds < 3600) {
            $minutes = round($seconds / 60);
            return "{$minutes} " . ($minutes == 1 ? 'minut' : 'minute');
        }
        $hours = round($seconds / 3600);
        return "{$hours} " . ($hours == 1 ? 'oră' : 'ore');
    }

    /**
     * Verificare rapidă pentru AJAX (un singur server)
     */
    public function quickCheck(Request $request)
    {
        $request->validate([
            'domain' => 'required|string',
            'type' => 'required|in:A,AAAA,MX,NS,TXT,CNAME',
        ]);

        $result = $this->queryDnsServer($request->domain, $request->type, '8.8.8.8');

        return response()->json([
            'success' => $result['status'] === 'success',
            'records' => $result['records'] ?? [],
            'response_time' => $result['response_time'] ?? null,
        ]);
    }
}
