<?php
namespace Crawl\Services;

/**
 * Fetches and validates robots.txt + XML sitemaps for a domain.
 * Also cross-references sitemap URLs against crawled pages.
 */
class SiteHealthService
{
    private int $timeout = 10;

    public function analyse(string $domain, array $crawledPages): array
    {
        $domain = rtrim($domain, '/');

        return [
            'robots'  => $this->checkRobots($domain),
            'sitemap' => $this->checkSitemap($domain, $crawledPages),
        ];
    }

    // ── Robots.txt ──────────────────────────────────────────────────────────

    private function checkRobots(string $domain): array
    {
        $url    = $domain . '/robots.txt';
        $result = $this->fetch($url);

        if (!$result['ok']) {
            return [
                'found'    => false,
                'status'   => $result['status'],
                'issues'   => ['robots.txt not found or inaccessible (HTTP ' . $result['status'] . ')'],
                'disallows'=> [],
                'sitemaps' => [],
                'raw'      => '',
            ];
        }

        $body      = $result['body'];
        $disallows = [];
        $sitemaps  = [];
        $issues    = [];

        foreach (explode("\n", $body) as $line) {
            $line = trim($line);
            if (stripos($line, 'Disallow:') === 0) {
                $path = trim(substr($line, 9));
                if ($path !== '') $disallows[] = $path;
            }
            if (stripos($line, 'Sitemap:') === 0) {
                $sitemaps[] = trim(substr($line, 8));
            }
        }

        // Flag common problematic disallows
        $criticalPaths = ['/', '/wp-admin/', '/wp-content/', '/api/'];
        foreach ($criticalPaths as $cp) {
            if (in_array($cp, $disallows, true)) {
                $issues[] = "Disallow: {$cp} — may block important content from being crawled";
            }
        }

        if (empty($sitemaps)) {
            $issues[] = 'No Sitemap directive found in robots.txt';
        }

        return [
            'found'    => true,
            'status'   => $result['status'],
            'issues'   => $issues,
            'disallows'=> $disallows,
            'sitemaps' => $sitemaps,
            'raw'      => substr($body, 0, 3000),
        ];
    }

    // ── Sitemap ─────────────────────────────────────────────────────────────

    private function checkSitemap(string $domain, array $crawledPages): array
    {
        $candidates = [$domain . '/sitemap.xml', $domain . '/sitemap_index.xml'];
        $result     = null;

        foreach ($candidates as $url) {
            $r = $this->fetch($url);
            if ($r['ok']) { $result = $r; $result['url'] = $url; break; }
        }

        if ($result === null) {
            return [
                'found'        => false,
                'url'          => '',
                'total_urls'   => 0,
                'issues'       => [],
                'not_crawled'  => [],
                'error_in_map' => [],
            ];
        }

        $sitemapUrls = $this->parseSitemapXml($result['body']);
        $crawledSet  = array_flip(array_column($crawledPages, 'url'));

        $notCrawled  = [];
        $errorInMap  = [];

        foreach ($sitemapUrls as $surl) {
            if (!isset($crawledSet[$surl])) {
                $notCrawled[] = $surl;
            }
        }

        // Find pages in sitemap that were crawled with non-200
        $crawledIndex = array_column($crawledPages, null, 'url');
        foreach ($sitemapUrls as $surl) {
            if (isset($crawledIndex[$surl])) {
                $sc = (int)($crawledIndex[$surl]['status_code'] ?? 0);
                if ($sc !== 200) {
                    $errorInMap[] = ['url' => $surl, 'status' => $sc];
                }
            }
        }

        $issues = [];
        if (!empty($errorInMap)) {
            $issues[] = count($errorInMap) . ' sitemap URL(s) return non-200 status — remove or fix them';
        }
        if (count($notCrawled) > 50) {
            $issues[] = count($notCrawled) . ' sitemap URL(s) were not reached during crawl — consider increasing crawl depth/pages';
        }

        return [
            'found'        => true,
            'url'          => $result['url'],
            'total_urls'   => count($sitemapUrls),
            'issues'       => $issues,
            'not_crawled'  => array_slice($notCrawled, 0, 20),
            'error_in_map' => array_slice($errorInMap, 0, 20),
        ];
    }

    private function parseSitemapXml(string $xml): array
    {
        $urls = [];
        libxml_use_internal_errors(true);
        $doc = simplexml_load_string($xml);
        if (!$doc) return $urls;

        // Standard sitemap
        foreach ($doc->url ?? [] as $node) {
            $loc = trim((string)($node->loc ?? ''));
            if ($loc !== '') $urls[] = $loc;
        }
        // Sitemap index
        foreach ($doc->sitemap ?? [] as $node) {
            $loc = trim((string)($node->loc ?? ''));
            if ($loc !== '') $urls[] = $loc;
        }

        return $urls;
    }

    // ── HTTP helper ─────────────────────────────────────────────────────────

    private function fetch(string $url): array
    {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => $this->timeout,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_MAXREDIRS      => 3,
            CURLOPT_USERAGENT      => 'Savit AuditBot/1.0',
            CURLOPT_SSL_VERIFYPEER => false,
        ]);
        $body   = (string)curl_exec($ch);
        $status = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $err    = curl_error($ch);
        curl_close($ch);

        return ['ok' => $status >= 200 && $status < 400 && $err === '', 'status' => $status, 'body' => $body];
    }
}
