<?php
/**
 * HTML_Analyzer - Phát hiện mã độc trong file HTML
 */
if (!defined('ABSPATH')) exit;

class Foxblock_HTML_Analyzer
{
    private string $rel_path;
    private string $source;
    private array  $findings = array();
    private ?string $site_domain = null;

    public function __construct(string $rel_path, string $source)
    {
        $this->rel_path = $rel_path;
        $this->source   = $source;
        $this->site_domain = parse_url(home_url(), PHP_URL_HOST);
    }

    public function analyze(): array
    {
        $this->findings = array();
        
        $this->detect_hidden_iframe();
        $this->detect_redirect();
        $this->detect_external_scripts();
        
        return $this->deduplicate($this->findings);
    }

    private function detect_hidden_iframe(): void
    {
        $lines = explode("\n", $this->source);
        
        foreach ($lines as $line_no => $line) {
            // iframe với display:none hoặc visibility:hidden
            if (preg_match('/<iframe[^>]*>/i', $line)) {
                $has_hidden = preg_match('/display\s*:\s*none|visibility\s*:\s*hidden|width\s*[:=]\s*["\']?0|height\s*[:=]\s*["\']?0/i', $line);
                
                if ($has_hidden) {
                    $this->add_finding(
                        $line_no + 1,
                        9,
                        'Hidden iframe - thường dùng để inject mã độc',
                        'high',
                        trim($line),
                        'heuristic'
                    );
                } else {
                    // iframe thường vẫn đáng ngờ
                    $this->add_finding(
                        $line_no + 1,
                        5,
                        'iframe phát hiện - kiểm tra nguồn',
                        'medium',
                        trim($line),
                        'heuristic'
                    );
                }
            }
        }
    }

    private function detect_redirect(): void
    {
        $lines = explode("\n", $this->source);
        
        foreach ($lines as $line_no => $line) {
            // window.location redirect
            if (preg_match('/window\.location\s*=\s*[\'"]([^\'"]+)[\'"]/i', $line, $match)) {
                $url = $match[1];
                $is_external = $this->is_external_url($url);
                
                $this->add_finding(
                    $line_no + 1,
                    $is_external ? 8 : 4,
                    'JavaScript redirect tới: ' . substr($url, 0, 50),
                    $is_external ? 'high' : 'medium',
                    trim($line),
                    'custom'
                );
            }

            // meta refresh
            if (preg_match('/<meta[^>]+http-equiv\s*=\s*[\'"]refresh[\'"][^>]*content\s*=\s*[\'"][^;]*;\s*url=([^\'"]+)[\'"]/i', $line, $match)) {
                $url = $match[1];
                $is_external = $this->is_external_url($url);
                
                $this->add_finding(
                    $line_no + 1,
                    $is_external ? 8 : 4,
                    'Meta refresh redirect tới: ' . substr($url, 0, 50),
                    $is_external ? 'high' : 'medium',
                    trim($line),
                    'custom'
                );
            }

            // location.href, location.replace
            if (preg_match('/location\.(?:href|replace)\s*(?:=|\()\s*[\'"]([^\'"]+)[\'"]/i', $line, $match)) {
                $url = $match[1];
                $is_external = $this->is_external_url($url);
                
                $this->add_finding(
                    $line_no + 1,
                    $is_external ? 7 : 3,
                    'Location redirect tới: ' . substr($url, 0, 50),
                    $is_external ? 'high' : 'low',
                    trim($line),
                    'custom'
                );
            }
        }
    }

    private function detect_external_scripts(): void
    {
        $lines = explode("\n", $this->source);
        
        foreach ($lines as $line_no => $line) {
            // <script src="http...">
            if (preg_match('/<script[^>]+src\s*=\s*[\'"]https?:\/\/([^\'"]+)[\'"]/i', $line, $match)) {
                $url = $match[1];
                $domain = parse_url('http://' . $url, PHP_URL_HOST);
                
                if ($this->is_external_domain($domain)) {
                    $is_suspicious = $this->is_suspicious_domain($domain);
                    
                    $this->add_finding(
                        $line_no + 1,
                        $is_suspicious ? 8 : 5,
                        'External script từ domain: ' . $domain,
                        $is_suspicious ? 'high' : 'medium',
                        trim($line),
                        'custom'
                    );
                }
            }
        }
    }

    private function is_external_url(string $url): bool
    {
        if (strpos($url, '//') === 0 || preg_match('/^https?:\/\//i', $url)) {
            $domain = parse_url($url, PHP_URL_HOST);
            return $this->is_external_domain($domain);
        }
        return false;
    }

    private function is_external_domain(?string $domain): bool
    {
        if (!$domain || !$this->site_domain) return true;
        return strpos($domain, $this->site_domain) === false;
    }

    private function is_suspicious_domain(?string $domain): bool
    {
        if (!$domain) return true;
        
        $safe_domains = array('googleapis.com', 'cloudflare.com', 'jquery.com', 'unpkg.com', 'cdnjs.com', 'bootstrapcdn.com');
        
        foreach ($safe_domains as $safe) {
            if (strpos($domain, $safe) !== false) {
                return false;
            }
        }
        
        return true;
    }

    private function add_finding(int $line, int $score, string $label, string $risk, string $code, string $method): void
    {
        $this->findings[] = array(
            'line'   => $line,
            'score'  => $score,
            'label'  => $label,
            'risk'   => $risk,
            'code'   => substr($code, 0, 200),
            'method' => $method,
        );
    }

    private function deduplicate(array $findings): array
    {
        $seen = array();
        $result = array();
        foreach ($findings as $f) {
            $key = $f['line'] . '|' . $f['label'];
            if (!isset($seen[$key])) {
                $seen[$key] = true;
                $result[] = $f;
            }
        }
        usort($result, fn($a, $b) => $b['score'] - $a['score']);
        return $result;
    }
}
