<?php

namespace UltraguardPro\modules\ip_intelligence;

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

use UltraguardPro\core\BaseController;
use UltraguardPro\core\DatabaseCrud;
use UltraguardPro\core\Utils;
use UltraguardPro\Plugin;
use UltraguardPro\support\LicenseInfo;
use UltraguardPro\support\SecureRemoteTransport;
use UltraguardPro\support\ServerCommunication;

class FeedSyncEngine extends BaseController {

    private const DAILY_BLOCK_OPTION = 'ip_intelligence_daily_blocks';
    private const BACKGROUND_LOOKUP_HOOK = 'ultraguard_ip_intel_refresh_ip';
    private const BACKGROUND_LOOKUP_TRANSIENT_PREFIX = 'ultraguard_ip_intel_lookup_';

    public function __construct( ?Plugin $plugin = null ) {
        parent::__construct( $plugin );
    }

    public function sync_all_feeds(): array {
        $summary = [
            'abuseipdb'          => 0,
            'spamhaus'           => 0,
            'ultraguard_network' => 0,
            'total'              => 0,
            'synced_at'          => current_time( 'mysql', true ),
        ];

        $api_key = trim( (string) $this->plugin->settings->get_option( 'ip_intelligence_abuseipdb_api_key', '' ) );
        if ( $api_key ) {
            $summary['abuseipdb'] = $this->sync_abuseipdb( $api_key );
        }

        if ( $this->plugin->settings->get_option( 'ip_intelligence_enable_spamhaus', true ) ) {
            $summary['spamhaus'] = $this->sync_spamhaus_zen();
        }

        if ( $this->plugin->settings->get_option( 'ip_intelligence_enable_ultraguard_network', true ) ) {
            $summary['ultraguard_network'] = $this->sync_ultraguard_network();
        }

        $summary['total'] = $summary['abuseipdb'] + $summary['spamhaus'] + $summary['ultraguard_network'];
        update_option( 'ip_intelligence_last_sync', $summary['synced_at'], false );
        update_option( 'ip_intelligence_last_sync_summary', $summary, false );

        if ( $summary['total'] > 0 && $this->plugin->settings->get_option( 'ip_intelligence_alert_on_high_confidence', true ) ) {
            do_action( 'ultraguard_pro/ip_intelligence/high_confidence_added', $summary );
        }

        return $summary;
    }

    public function sync_abuseipdb( string $api_key ): int {
        $response = SecureRemoteTransport::request_json( 'GET', 'https://api.abuseipdb.com/api/v2/blacklist?confidenceMinimum=75&limit=10000', [
            'timeout' => 20,
            'headers' => [
                'Accept' => 'application/json',
                'Key'    => $api_key,
            ],
        ] );

        if ( ! $response['success'] ) {
            return 0;
        }

        $payload = is_array( $response['body'] ?? null ) ? $response['body'] : [];
        $rows    = $payload['data'] ?? [];
        $count   = 0;

        foreach ( $rows as $row ) {
            if ( $this->upsert_feed_record( [
                'ip_address'      => $row['ipAddress'] ?? '',
                'ip_type'         => filter_var( $row['ipAddress'] ?? '', FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ? 'ipv6' : 'ipv4',
                'threat_category' => 'exploit',
                'confidence'      => absint( $row['abuseConfidenceScore'] ?? 75 ),
                'source'          => 'abuseipdb',
                'country_code'    => sanitize_text_field( $row['countryCode'] ?? '' ),
                'is_active'       => 1,
                'expires_at'      => null,
                'last_seen_at'    => current_time( 'mysql', true ),
                'reports_count'   => absint( $row['totalReports'] ?? 0 ),
            ] ) ) {
                $count++;
            }
        }

        return $count;
    }

    public function sync_spamhaus_zen(): int {
        $response = SecureRemoteTransport::request( 'GET', 'https://www.spamhaus.org/drop/drop.txt', [
            'timeout'   => 20,
            'sslverify' => true,
        ] );

        if ( is_wp_error( $response ) ) {
            return 0;
        }

        $code = (int) wp_remote_retrieve_response_code( $response );
        if ( $code < 200 || $code >= 300 ) {
            return 0;
        }

        $body  = (string) wp_remote_retrieve_body( $response );
        $lines = preg_split( '/\r\n|\r|\n/', $body );

        $count = 0;
        foreach ( (array) $lines as $line ) {
            $line = trim( (string) $line );
            if ( '' === $line || '#' === $line[0] || ';' === $line[0] ) {
                continue;
            }

            $parts = preg_split( '/\s*;\s*/', $line );
            $cidr  = sanitize_text_field( (string) ( $parts[0] ?? '' ) );
            if ( '' === $cidr ) {
                continue;
            }

            $ip_type = str_contains( $cidr, ':' ) ? 'ipv6' : 'ipv4';
            if ( str_contains( $cidr, '/' ) ) {
                $ip_type = 'cidr';
            }

            if ( $this->upsert_feed_record( [
                'ip_address'      => $cidr,
                'ip_type'         => $ip_type,
                'threat_category' => 'spam',
                'confidence'      => 95,
                'source'          => 'spamhaus',
                'country_code'    => '',
                'is_active'       => 1,
                'expires_at'      => gmdate( 'Y-m-d H:i:s', time() + 7 * DAY_IN_SECONDS ),
                'last_seen_at'    => current_time( 'mysql', true ),
                'reports_count'   => 1,
            ] ) ) {
                $count++;
            }
        }

        return $count;
    }

    public function sync_ultraguard_network(): int {
        $license_data  = get_option( LicenseInfo::OPTION_DATA, [] );
        $identity_key  = (string) get_option( LicenseInfo::OPTION_KEY, '' );
        $transport_key = (
            is_array( $license_data )
            && ! empty( $license_data['transport_key'] )
            && '' !== $identity_key
            && hash( 'sha256', $identity_key ) === (string) ( $license_data['transport_identity_fingerprint'] ?? '' )
        )
            ? (string) $license_data['transport_key']
            : (string) get_option( LicenseInfo::OPTION_KEY, '' );
        if ( '' === $transport_key ) {
            $transport_key = 'ultraguard-public';
        }
        if ( '' === $identity_key ) {
            $identity_key = 'ultraguard-public';
        }

        $url = add_query_arg( [
            'site_url'   => home_url(),
            'is_pro'     => LicenseInfo::instance()->isPro() ? '1' : '0',
            'plugin_ver' => defined( 'ULTRAGUARD_PRO_VERSION' ) ? ULTRAGUARD_PRO_VERSION : '',
            'ts'         => time(),
        ], ServerCommunication::API_BASE . ServerCommunication::EP_PULL_BLACKLIST );

        $result = SecureRemoteTransport::signed_request_json(
            'GET',
            $url,
            ServerCommunication::EP_PULL_BLACKLIST,
            [],
            $transport_key,
            home_url(),
            'UltraGuardPro/' . ( defined( 'ULTRAGUARD_PRO_VERSION' ) ? ULTRAGUARD_PRO_VERSION : '1.0' ),
            [
                'timeout'   => 20,
                'sslverify' => true,
            ],
            false,
            $identity_key
        );

        if ( ! $result['success'] ) {
            return 0;
        }

        $body    = is_array( $result['body'] ?? null ) ? $result['body'] : [];
        $entries = is_array( $body['entries'] ?? null ) ? $body['entries'] : [];

        $count = 0;
        foreach ( $entries as $row ) {
            $ip_address = sanitize_text_field( (string) ( $row['ip_address'] ?? $row['ip'] ?? '' ) );
            if ( '' === $ip_address ) {
                continue;
            }

            $confidence = absint( $row['confidence'] ?? $row['score'] ?? 85 );
            $ip_type    = sanitize_key( (string) ( $row['ip_type'] ?? '' ) );
            if ( '' === $ip_type ) {
                $ip_type = str_contains( $ip_address, '/' )
                    ? 'cidr'
                    : ( filter_var( $ip_address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ? 'ipv6' : 'ipv4' );
            }

            if ( $this->upsert_feed_record( [
                'ip_address'      => $ip_address,
                'ip_type'         => $ip_type,
                'threat_category' => sanitize_key( (string) ( $row['threat_category'] ?? $row['category'] ?? 'scanner' ) ),
                'confidence'      => max( 1, min( 100, $confidence ) ),
                'source'          => 'ultraguard_network',
                'country_code'    => sanitize_text_field( (string) ( $row['country_code'] ?? $row['country'] ?? '' ) ),
                'is_active'       => 1,
                'expires_at'      => ! empty( $row['expires_at'] ) ? sanitize_text_field( (string) $row['expires_at'] ) : null,
                'last_seen_at'    => current_time( 'mysql', true ),
                'reports_count'   => absint( $row['reports_count'] ?? 1 ),
            ] ) ) {
                $count++;
            }
        }

        return $count;
    }

    public function check_ip_reputation( string $ip ): array {
        $ip = sanitize_text_field( $ip );
        if ( ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
            return [ 'score' => 0, 'is_threat' => false, 'categories' => [] ];
        }

        $feed_table  = $this->getTable( 'threat_feed' );
        $cache_table = $this->getTable( 'ip_reputation_cache' );
        $categories  = [];
        $score       = 0;

        if ( $feed_table ) {
            $matches = $feed_table->get_by( [ 'ip_address' => $ip, 'is_active' => 1 ], 20, 'confidence', 'DESC' );
            foreach ( $matches as $row ) {
                $categories[] = $row['threat_category'];
                $score        = max( $score, (int) ( $row['confidence'] ?? 0 ) );
            }
        }

        if ( $cache_table ) {
            $cached = $cache_table->get_row( [ 'ip_address' => $ip ] );
            if ( ! empty( $cached['expires_at'] ) && strtotime( $cached['expires_at'] ) > time() ) {
                $score = max( $score, (int) ( $cached['abuse_score'] ?? 0 ) );
                $extra = json_decode( (string) ( $cached['threat_types'] ?? '[]' ), true );
                if ( is_array( $extra ) ) {
                    $categories = array_merge( $categories, $extra );
                }
            }
        }

        return [
            'score'      => $score,
            'is_threat'  => $score > 0,
            'categories' => array_values( array_unique( array_filter( $categories ) ) ),
        ];
    }

    public function lookup_ip_abuse_score( string $ip, string $api_key, bool $allow_remote = true ): array {
        $ip = sanitize_text_field( $ip );
        $cache_table = $this->getTable( 'ip_reputation_cache' );
        $cached      = $this->get_cached_lookup_result( $ip );

        if ( null !== $cached ) {
            return $cached;
        }

        if ( ! $allow_remote ) {
            return $this->empty_lookup_result( $ip );
        }

        $response = SecureRemoteTransport::request_json( 'GET', 'https://api.abuseipdb.com/api/v2/check?ipAddress=' . rawurlencode( $ip ) . '&maxAgeInDays=90&verbose', [
            'timeout' => 15,
            'headers' => [
                'Accept' => 'application/json',
                'Key'    => $api_key,
            ],
        ] );

        if ( ! $response['success'] ) {
            return [ 'ip' => $ip, 'abuse_score' => 0, 'is_threat' => false, 'threat_types' => [], 'country' => '', 'isp' => '', 'is_tor' => false, 'is_vpn' => false, 'cached' => false ];
        }

        $body  = is_array( $response['body'] ?? null ) ? $response['body'] : [];
        $data  = $body['data'] ?? [];
        $types = [];

        if ( ! empty( $data['reports'] ) && is_array( $data['reports'] ) ) {
            foreach ( $data['reports'] as $report ) {
                if ( ! empty( $report['categories'] ) ) {
                    $types[] = implode( ',', array_map( 'strval', (array) $report['categories'] ) );
                }
            }
        }

        $result = [
            'ip'          => $ip,
            'abuse_score' => absint( $data['abuseConfidenceScore'] ?? 0 ),
            'is_threat'   => absint( $data['abuseConfidenceScore'] ?? 0 ) > 0,
            'threat_types'=> array_values( array_unique( array_filter( $types ) ) ),
            'country'     => sanitize_text_field( $data['countryCode'] ?? '' ),
            'isp'         => sanitize_text_field( $data['isp'] ?? '' ),
            'is_tor'      => ! empty( $data['isTor'] ),
            'is_vpn'      => ! empty( $data['isVpn'] ),
            'cached'      => false,
        ];

        if ( $cache_table && $this->plugin->settings->get_option( 'ip_intelligence_cache_lookups', true ) ) {
            $hours = max( 1, (int) $this->plugin->settings->get_option( 'ip_intelligence_cache_duration_hours', 24 ) );
            $cache_table->upsert_cache( [
                'ip_address'   => $ip,
                'abuse_score'  => $result['abuse_score'],
                'is_threat'    => $result['is_threat'] ? 1 : 0,
                'threat_types' => wp_json_encode( $result['threat_types'] ),
                'country_code' => $result['country'],
                'isp'          => $result['isp'],
                'is_tor'       => $result['is_tor'] ? 1 : 0,
                'is_vpn'       => $result['is_vpn'] ? 1 : 0,
                'checked_at'   => current_time( 'mysql', true ),
                'expires_at'   => gmdate( 'Y-m-d H:i:s', time() + $hours * HOUR_IN_SECONDS ),
            ] );
        }

        return $result;
    }

    public function prune_expired(): void {
        $now = current_time( 'mysql', true );
        DatabaseCrud::delete_before_datetime( 'ultraguard_threat_feed', 'expires_at', $now, true, true );
        DatabaseCrud::delete_before_datetime( 'ultraguard_ip_reputation_cache', 'expires_at', $now, false, true );
    }

    public function maybe_check_request_ip(): void {
        $ip = Utils::get_client_ip();
        if ( ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
            return;
        }

        $reputation = $this->check_ip_reputation( $ip );
        $threshold  = max( 0, min( 100, (int) $this->plugin->settings->get_option( 'ip_intelligence_abuse_score_threshold', 75 ) ) );

        if ( $this->plugin->settings->get_option( 'ip_intelligence_block_tor', false ) || $this->plugin->settings->get_option( 'ip_intelligence_block_vpn', false ) ) {
            $api_key = trim( (string) $this->plugin->settings->get_option( 'ip_intelligence_abuseipdb_api_key', '' ) );
            if ( $api_key ) {
                $live = $this->lookup_ip_abuse_score( $ip, $api_key, false );
                if ( ! empty( $live['cached'] ) ) {
                    if ( $this->plugin->settings->get_option( 'ip_intelligence_block_tor', false ) && ! empty( $live['is_tor'] ) ) {
                        do_action( 'ultraguard_pro/firewall/block_ip', $ip, 'ip_intelligence', 'Tor exit node blocked' );
                        $this->increment_block_count();
                        return;
                    }
                    if ( $this->plugin->settings->get_option( 'ip_intelligence_block_vpn', false ) && ! empty( $live['is_vpn'] ) ) {
                        do_action( 'ultraguard_pro/firewall/block_ip', $ip, 'ip_intelligence', 'VPN range blocked' );
                        $this->increment_block_count();
                        return;
                    }
                    $reputation['score'] = max( $reputation['score'], $live['abuse_score'] );
                    $reputation['categories'] = array_values( array_unique( array_merge( $reputation['categories'], $live['threat_types'] ) ) );
                } else {
                    $this->queue_background_lookup( $ip );
                }
            }
        }

        if ( $reputation['score'] >= $threshold ) {
            $reason = sprintf( 'Threat score %d (%s)', $reputation['score'], implode( ', ', $reputation['categories'] ) );
            do_action( 'ultraguard_pro/firewall/block_ip', $ip, 'ip_intelligence', $reason );
            $this->increment_block_count();
        }
    }

    public function get_stats(): array {
        $today = gmdate( 'Y-m-d 00:00:00' );
        $days  = [];
        for ( $i = 29; $i >= 0; $i-- ) {
            $key = gmdate( 'Y-m-d', strtotime( '-' . $i . ' days' ) );
            $days[ $key ] = (int) ( get_option( self::DAILY_BLOCK_OPTION, [] )[ $key ] ?? 0 );
        }

        $categories = DatabaseCrud::get_grouped_count( 'ultraguard_threat_feed', 'threat_category', [], 10 );
        $categories = array_map(
            static function ( $row ): array {
                if ( is_object( $row ) ) {
                    return [
                        'threat_category' => (string) ( $row->threat_category ?? '' ),
                        'total'           => (int) ( $row->count ?? 0 ),
                    ];
                }

                return [
                    'threat_category' => (string) ( $row['threat_category'] ?? '' ),
                    'total'           => (int) ( $row['count'] ?? 0 ),
                ];
            },
            (array) $categories
        );

        return [
            'feed_total'   => (int) DatabaseCrud::get_count( 'ultraguard_threat_feed' ),
            'synced_today' => DatabaseCrud::count_since( 'ultraguard_threat_feed', [], 'created_at', $today ),
            'blocked_24h'  => (int) ( get_option( self::DAILY_BLOCK_OPTION, [] )[ gmdate( 'Y-m-d' ) ] ?? 0 ),
            'categories'   => $categories,
            'blocks_30d'   => $days,
        ];
    }

    private function upsert_feed_record( array $row ): bool {
        $table = $this->getTable( 'threat_feed' );
        return $table && method_exists( $table, 'upsert_feed_entry' ) ? $table->upsert_feed_entry( $row ) : false;
    }

    private function increment_block_count(): void {
        $data = get_option( self::DAILY_BLOCK_OPTION, [] );
        $key  = gmdate( 'Y-m-d' );
        $data[ $key ] = (int) ( $data[ $key ] ?? 0 ) + 1;
        if ( count( $data ) > 40 ) {
            ksort( $data );
            $data = array_slice( $data, -40, null, true );
        }
        update_option( self::DAILY_BLOCK_OPTION, $data, false );
    }

    private function get_cached_lookup_result( string $ip ): ?array {
        $cache_table = $this->getTable( 'ip_reputation_cache' );
        if ( ! $cache_table || ! $this->plugin->settings->get_option( 'ip_intelligence_cache_lookups', true ) ) {
            return null;
        }

        $cached = $cache_table->get_row( [ 'ip_address' => $ip ] );
        if ( empty( $cached['expires_at'] ) || strtotime( $cached['expires_at'] ) <= time() ) {
            return null;
        }

        return [
            'ip'           => $ip,
            'abuse_score'  => (int) ( $cached['abuse_score'] ?? 0 ),
            'is_threat'    => ! empty( $cached['is_threat'] ),
            'threat_types' => json_decode( (string) ( $cached['threat_types'] ?? '[]' ), true ) ?: [],
            'country'      => (string) ( $cached['country_code'] ?? '' ),
            'isp'          => (string) ( $cached['isp'] ?? '' ),
            'is_tor'       => ! empty( $cached['is_tor'] ),
            'is_vpn'       => ! empty( $cached['is_vpn'] ),
            'cached'       => true,
        ];
    }

    private function empty_lookup_result( string $ip ): array {
        return [
            'ip'           => $ip,
            'abuse_score'  => 0,
            'is_threat'    => false,
            'threat_types' => [],
            'country'      => '',
            'isp'          => '',
            'is_tor'       => false,
            'is_vpn'       => false,
            'cached'       => false,
        ];
    }

    private function queue_background_lookup( string $ip ): void {
        $lock_key = self::BACKGROUND_LOOKUP_TRANSIENT_PREFIX . md5( $ip );
        if ( get_transient( $lock_key ) ) {
            return;
        }

        set_transient( $lock_key, 1, 10 * MINUTE_IN_SECONDS );

        if ( ! wp_next_scheduled( self::BACKGROUND_LOOKUP_HOOK, [ $ip ] ) ) {
            wp_schedule_single_event( time() + MINUTE_IN_SECONDS, self::BACKGROUND_LOOKUP_HOOK, [ $ip ] );
        }
    }
}
