<?php

declare(strict_types=1);

namespace SiteFort\Modules\Scanner;

use SiteFort\Includes\RemoteUrlValidator;
use SiteFort\Modules\Cloud\Services\CloudScanner;
use SiteFort\Modules\Scanner\Repository\IssueRepository;

if ( ! defined( 'ABSPATH' ) ) { exit; }

/**
 * Run reputation and blocklist checks through the Cloud API.
 */
class ReputationScanner {

    /** @var CloudScanner */
    private $service;

    public function __construct() {
        $this->service = new CloudScanner();
    }

    public function get_title() {
        return 'Reputation & Blocklist';
    }

    /**
     * Run the reputation scan.
     *
     * @return array{issues: array, count: int}
     */
    public function run() {
        $issues = [];

        if ( ! class_exists( 'SiteFort\\Modules\\Scanner\\Repository\\IssueRepository' ) ) {
            require_once __DIR__ . '/Repository/IssueRepository.php';
        }

        $site_url  = get_site_url();
        $domain    = wp_parse_url( $site_url, PHP_URL_HOST );
        $domain    = is_string( $domain ) ? $domain : '';
        $server_ip = $this->resolve_server_ip( $domain );

        // Collect external links from the site's homepage.
        $ext_links = $this->collect_external_links( $site_url, $domain );

        // Call the Cloud reputation API.
        $result = $this->service->check_reputation( $server_ip, $domain, $ext_links );

        if ( is_wp_error( $result ) ) {
            sitefort_log( 'SiteFort: Cloud reputation check failed: ' . $result->get_error_message() );
            return [ 'issues' => [], 'count' => 0 ];
        }

        // --- Process IP Reputation ---
        $ip_rep = $result['ip_reputation'] ?? [];
        if ( ! empty( $ip_rep ) ) {
            $this->process_ip_reputation( $ip_rep, $server_ip, $issues );
        }

        // --- Process Domain Reputation ---
        $domain_rep          = $result['domain_reputation'] ?? [];
        $domain_bl_count     = (int) ( $result['domain_blacklist_count'] ?? 0 );
        if ( $domain_bl_count > 0 && ! empty( $domain_rep ) ) {
            $this->process_domain_reputation( $domain_rep, $domain, $issues );
        }

        // --- Process External Link Blacklist ---
        $ext_bl       = $result['ext_blacklist'] ?? [];
        $ext_bl_count = (int) ( $result['ext_blacklist_count'] ?? 0 );
        if ( $ext_bl_count > 0 && ! empty( $ext_bl ) ) {
            $this->process_ext_blacklist( $ext_bl, $issues );
        }

        return [
            'issues' => $issues,
            'count'  => count( $issues ),
        ];
    }

    // =========================================================================
    // IP Reputation
    // =========================================================================

    /**
     * Process ip_reputation from the API response.
     */
    private function process_ip_reputation( array $ip_rep, string $server_ip, array &$issues ): void {
        $listed_on = $ip_rep['listed_on'] ?? [];
        $dnsbl     = $ip_rep['dnsbl'] ?? [];
        // 1. DNSBL listings
        if ( ! empty( $listed_on ) ) {
            $sources = implode( ', ', $listed_on );
            $issues[] = $this->build_issue(
                'ip_dnsbl',
                'suspicious',
                'high',
                sprintf(
                    'Server IP (%s) is listed on DNS blocklists: %s. This may cause email deliverability issues and indicate a compromised network.',
                    $server_ip,
                    $sources
                ),
                'DNSBL',
                [
                    'ip'        => $server_ip,
                    'listed_on' => $listed_on,
                    'dnsbl'     => $dnsbl,
                    'title'     => 'Server IP Listed on DNS Blocklists',
                    'remedy'    => [
                        'Contact your hosting provider to request IP delisting.',
                        'Verify no outbound spam or malicious traffic originates from your server.',
                        'Consider moving to a dedicated IP address if on a shared host.',
                    ],
                ]
            );
        }

    }

    // =========================================================================
    // Domain Reputation
    // =========================================================================

    /**
     * Process domain_reputation from the API response.
     */
    private function process_domain_reputation( array $domain_rep, string $domain, array &$issues ): void {
        $issues[] = $this->build_issue(
            'domain_blacklist',
            'infected',
            $domain_rep['severity'] ?? 'critical',
            $domain_rep['impact'] ?? sprintf( 'Domain %s is blacklisted.', $domain ),
            'Domain Blacklist',
            [
                'domain'  => $domain,
                'title'   => $domain_rep['title'] ?? 'Domain Blacklisted',
                'impact'  => $domain_rep['impact'] ?? '',
                'remedy'  => $domain_rep['remedy'] ?? [],
                'proof'   => $domain_rep['proof'] ?? [],
            ]
        );
    }

    // =========================================================================
    // External Link Blacklist
    // =========================================================================

    /**
     * Process ext_blacklist from the API response.
     */
    private function process_ext_blacklist( array $ext_bl, array &$issues ): void {
        $issues[] = $this->build_issue(
            'ext_blacklist',
            'suspicious',
            $ext_bl['severity'] ?? 'high',
            $ext_bl['impact'] ?? 'Blacklisted external domains referenced on the site.',
            'External Links',
            [
                'title'  => $ext_bl['title'] ?? 'Blacklisted External Domain References',
                'impact' => $ext_bl['impact'] ?? '',
                'remedy' => $ext_bl['remedy'] ?? [],
                'proof'  => $ext_bl['proof'] ?? [],
            ]
        );
    }

    // =========================================================================
    // Helpers
    // =========================================================================

    /**
     * Build a normalised issue array ready for IssueRepository::stage().
     */
    private function build_issue( string $stable_key, string $status, string $severity, string $message, string $source, array $data ): array {
        return [
            'id'       => $stable_key,
            'hash'     => IssueRepository::generateHash( 'blocklist', $stable_key ),
            'type'     => 'blocklist',
            'status'   => $status,
            'severity' => $severity,
            'message'  => $message,
            'file'     => $source,
            'data'     => $data,
        ];
    }

    /**
     * Resolve the server IP for the site domain.
     *
     * Falls back to SERVER_ADDR if DNS resolution returns the domain itself
     * (common on some hosting where gethostbyname fails).
     */
    private function resolve_server_ip( string $domain ): string {
        $ip = gethostbyname( $domain );

        // gethostbyname returns the input string on failure.
        if ( $ip === $domain || ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
            $server_addr = filter_input( INPUT_SERVER, 'SERVER_ADDR', FILTER_VALIDATE_IP );
            $server_addr = is_string( $server_addr ) ? $server_addr : '';
            if ( '' !== $server_addr && filter_var( $server_addr, FILTER_VALIDATE_IP ) ) {
                return $server_addr;
            }
            return '';
        }

        return $ip;
    }

    /**
     * Collect external links from the site homepage.
     *
     * Fetches the homepage HTML and extracts unique external domains
     * from href and src attributes. Internal links and common CDN/platform
     * domains are excluded by the Cloud API's built-in whitelist.
     *
     * @param string $site_url Site URL.
     * @param string $domain   Site domain (used to filter internal links).
     * @return string[] List of unique external URLs.
     */
    private function collect_external_links( string $site_url, string $domain ): array {
        $args = [
            'timeout'   => 10,
            'sslverify' => true,
            'headers'   => [ 'User-Agent' => 'SiteFort/1.0 Security Scanner' ],
        ];

        $response = wp_remote_get( $site_url, $args );

        if ( is_wp_error( $response ) && RemoteUrlValidator::is_tls_error( $response ) ) {
            $args['sslverify'] = false;
            $response          = wp_remote_get( $site_url, $args );
        }

        if ( is_wp_error( $response ) ) {
            return [];
        }

        $body = wp_remote_retrieve_body( $response );
        if ( empty( $body ) ) {
            return [];
        }

        $links   = [];
        $domain  = strtolower( $domain );

        // Match href="..." and src="..." attributes for http(s) URLs.
        if ( preg_match_all( '/(?:href|src)\s*=\s*["\']?(https?:\/\/[^"\'>\s]+)/i', $body, $matches ) ) {
            foreach ( $matches[1] as $url ) {
                $host = strtolower( (string) wp_parse_url( $url, PHP_URL_HOST ) );
                if ( empty( $host ) ) {
                    continue;
                }
                // Skip internal links.
                if ( $host === $domain || str_ends_with( $host, '.' . $domain ) ) {
                    continue;
                }
                $links[ $host ] = $url;
            }
        }

        // The API accepts up to 15 external domains; keep it bounded.
        return array_values( array_slice( $links, 0, 15 ) );
    }
}
