<?php

namespace UltraguardPro\modules\uptime_monitor;

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

use UltraguardPro\core\BaseController;
use UltraguardPro\Plugin;

class UptimeCheckEngine extends BaseController {

    private const INTERNAL_REQUEST_ACTION = 'uptime-monitor';

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

    public function check_uptime( string $url ): array {
        $table    = $this->getTable( 'uptime_checks' );
        $previous = $table && method_exists( $table, 'get_latest_for_url' ) ? $table->get_latest_for_url( $url ) : null;
        $start    = microtime( true );
        $response = wp_remote_get( $url, [
            'timeout'     => 15,
            'redirection' => 3,
            'user-agent'  => $this->get_monitor_user_agent(),
            'headers'     => [
                'X-UltraGuard-Internal'       => self::INTERNAL_REQUEST_ACTION,
                'X-UltraGuard-Internal-Token' => $this->get_internal_request_token(),
            ],
        ] );
        $elapsed  = (int) round( ( microtime( true ) - $start ) * 1000 );

        if ( is_wp_error( $response ) ) {
            $result = [
                'check_url'      => esc_url_raw( $url ),
                'status'         => 'timeout',
                'response_code'  => 0,
                'response_time'  => $elapsed,
                'error_message'  => $response->get_error_message(),
                'check_source'   => 'local',
                'created_at'     => current_time( 'mysql', true ),
            ];
        } else {
            $code   = (int) wp_remote_retrieve_response_code( $response );
            $status = $code >= 200 && $code < 400 ? 'up' : ( $code >= 500 ? 'down' : 'error' );

            $result = [
                'check_url'      => esc_url_raw( $url ),
                'status'         => $status,
                'response_code'  => $code,
                'response_time'  => $elapsed,
                'error_message'  => '',
                'check_source'   => 'local',
                'created_at'     => current_time( 'mysql', true ),
            ];
        }

        if ( $table ) {
            $table->insert( $result );
        }

        if ( is_array( $previous ) && ( $previous['status'] ?? '' ) !== $result['status'] ) {
            do_action( 'ultraguard_pro/uptime_monitor/status_changed', $previous, $result );
        }

        return $result;
    }

    private function get_monitor_user_agent(): string {
        $version = defined( 'ULTRAGUARD_PRO_VERSION' ) ? ULTRAGUARD_PRO_VERSION : '1.0.0';
        return 'UltraGuard-Uptime-Monitor/' . $version;
    }

    private function get_internal_request_token(): string {
        return wp_hash( self::INTERNAL_REQUEST_ACTION . '|' . untrailingslashit( home_url() ), 'auth' );
    }

    public function check_ssl( string $domain ): array {
        $table   = $this->getTable( 'ssl_checks' );
        $context = stream_context_create( [ 'ssl' => [ 'capture_peer_cert' => true, 'verify_peer' => false, 'verify_peer_name' => false ] ] );
        $client  = @stream_socket_client( 'ssl://' . $domain . ':443', $errno, $errstr, 15, STREAM_CLIENT_CONNECT, $context );

        if ( ! $client ) {
            $result = [
                'domain'        => sanitize_text_field( $domain ),
                'is_valid'      => 0,
                'expiry_date'   => null,
                'days_remaining'=> null,
                'issuer'        => '',
                'subject'       => '',
                'fingerprint'   => '',
                'error_message' => $errstr ? sanitize_text_field( $errstr ) : __( 'Unable to connect to SSL endpoint.', 'ultraguard-security-suite' ),
                'checked_at'    => current_time( 'mysql', true ),
            ];
        } else {
            $params = stream_context_get_params( $client );
            $cert   = $params['options']['ssl']['peer_certificate'] ?? null;
            $parsed = $cert ? openssl_x509_parse( $cert ) : false;

            if ( empty( $parsed['validTo_time_t'] ) ) {
                $result = [
                    'domain'        => sanitize_text_field( $domain ),
                    'is_valid'      => 0,
                    'expiry_date'   => null,
                    'days_remaining'=> null,
                    'issuer'        => '',
                    'subject'       => '',
                    'fingerprint'   => '',
                    'error_message' => __( 'Unable to parse SSL certificate details.', 'ultraguard-security-suite' ),
                    'checked_at'    => current_time( 'mysql', true ),
                ];
            } else {
                $expiry         = (int) $parsed['validTo_time_t'];
                $days_remaining = (int) floor( ( $expiry - time() ) / DAY_IN_SECONDS );
                $issuer         = ! empty( $parsed['issuer']['O'] ) ? (string) $parsed['issuer']['O'] : '';
                $subject        = ! empty( $parsed['subject']['CN'] ) ? (string) $parsed['subject']['CN'] : '';
                $fingerprint    = strtoupper( openssl_x509_fingerprint( $cert, 'sha256' ) );

                $result = [
                    'domain'         => sanitize_text_field( $domain ),
                    'is_valid'       => $days_remaining > 0 ? 1 : 0,
                    'expiry_date'    => gmdate( 'Y-m-d H:i:s', $expiry ),
                    'days_remaining' => $days_remaining,
                    'issuer'         => sanitize_text_field( $issuer ),
                    'subject'        => sanitize_text_field( $subject ),
                    'fingerprint'    => sanitize_text_field( $fingerprint ),
                    'error_message'  => '',
                    'checked_at'     => current_time( 'mysql', true ),
                ];
            }

            // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- closes the socket handle opened for the low-level SSL probe.
            fclose( $client );
        }

        if ( $table ) {
            $table->insert( $result );
        }

        do_action( 'ultraguard_pro/uptime_monitor/ssl_checked', $result );

        return $result;
    }

    public function get_uptime_percentage( int $hours = 24 ): float {
        $table = $this->getTable( 'uptime_checks' );
        return $table && method_exists( $table, 'calculate_uptime_percentage' ) ? (float) $table->calculate_uptime_percentage( $hours ) : 0.0;
    }

    public function get_average_response_time( int $hours = 24 ): float {
        $table = $this->getTable( 'uptime_checks' );
        return $table && method_exists( $table, 'calculate_average_response_time' ) ? (float) $table->calculate_average_response_time( $hours ) : 0.0;
    }

    public function get_current_status(): string {
        $table  = $this->getTable( 'uptime_checks' );
        $latest = $table && method_exists( $table, 'get_latest' ) ? $table->get_latest() : null;

        if ( empty( $latest['status'] ) ) {
            return 'degraded';
        }

        if ( in_array( $latest['status'], [ 'down', 'timeout', 'error' ], true ) ) {
            return 'down';
        }

        $average = $this->get_average_response_time( 24 );
        return $average > 2500 ? 'degraded' : 'up';
    }

    public function get_ssl_status(): array {
        $table = $this->getTable( 'ssl_checks' );
        return $table && method_exists( $table, 'get_latest_for_domain' ) ? (array) $table->get_latest_for_domain( (string) wp_parse_url( home_url(), PHP_URL_HOST ) ) : [];
    }

    public function get_recent_incidents( int $limit = 20 ): array {
        $table = $this->getTable( 'uptime_checks' );
        return $table && method_exists( $table, 'get_recent_incidents' ) ? (array) $table->get_recent_incidents( $limit ) : [];
    }

    public function get_response_chart(): array {
        $table = $this->getTable( 'uptime_checks' );
        return $table && method_exists( $table, 'get_response_time_chart' ) ? (array) $table->get_response_time_chart( 24 ) : [ 'labels' => [], 'values' => [] ];
    }

    public function get_daily_uptime_chart(): array {
        $table = $this->getTable( 'uptime_checks' );
        return $table && method_exists( $table, 'get_daily_uptime_chart' ) ? (array) $table->get_daily_uptime_chart( 30 ) : [ 'labels' => [], 'values' => [] ];
    }
}
