<?php
/**
 * Wally Monitor - WP-Cron Scheduled Scans
 *
 * Automatic periodic accessibility scans using WordPress cron.
 * Configurable frequency (daily/weekly/monthly) and email notifications.
 *
 * @package    WallyMonitor
 * @subpackage Includes
 * @since      1.1.0
 */

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

class Wally_Cron {

    /** @var string Cron hook name */
    const HOOK = 'wally_monitor_cron_scan';

    /** @var string Monthly recurrence name */
    const MONTHLY_RECURRENCE = 'wally_monthly';

    /**
     * Initialize cron hooks and custom schedules.
     */
    public static function init() {
        $instance = new self();

        // Register custom schedule (monthly)
        add_filter( 'cron_schedules', array( $instance, 'add_custom_schedules' ) );

        // Cron execution hook
        add_action( self::HOOK, array( $instance, 'run_scheduled_scan' ) );

        // Admin settings integration
        add_action( 'admin_init', array( $instance, 'register_settings' ) );
    }

    /**
     * Add monthly schedule to WordPress cron.
     *
     * @param array $schedules Existing schedules.
     * @return array Modified schedules.
     */
    public function add_custom_schedules( $schedules ) {
        $schedules[ self::MONTHLY_RECURRENCE ] = array(
            'interval' => 30 * DAY_IN_SECONDS,
            'display'  => __( 'Uma vez por mês', 'wally-monitor' ),
        );
        return $schedules;
    }

    /**
     * Enable scheduled scanning.
     *
     * @param string $frequency Recurrence: hourly, daily, weekly, monthly.
     */
    public static function enable( $frequency = 'weekly' ) {
        self::disable(); // Clear any existing schedule

        $recurrence = self::map_frequency( $frequency );
        wp_schedule_event( time() + 60, $recurrence, self::HOOK );
        update_option( 'wally_monitor_cron_enabled', true );
        update_option( 'wally_monitor_cron_frequency', $frequency );
    }

    /**
     * Disable scheduled scanning.
     */
    public static function disable() {
        wp_clear_scheduled_hook( self::HOOK );
        update_option( 'wally_monitor_cron_enabled', false );
    }

    /**
     * Check if cron scanning is enabled.
     *
     * @return bool
     */
    public static function is_enabled() {
        return (bool) get_option( 'wally_monitor_cron_enabled', false );
    }

    /**
     * Get next scheduled scan time.
     *
     * @return int|false Timestamp or false.
     */
    public static function get_next_run() {
        return wp_next_scheduled( self::HOOK );
    }

    /**
     * Execute the scheduled scan.
     * Called by WP-Cron.
     */
    public function run_scheduled_scan() {
        // Allow WordPress to use more memory for large scans.
        wp_raise_memory_limit( 'cron' );

        $settings = get_option( 'wally_monitor_settings', array(
            'post_types'   => array( 'post', 'page' ),
            'include_home' => true,
            'max_urls'     => 50, // Lower default for cron
        ) );

        $cron_settings = get_option( 'wally_monitor_cron_settings', array(
            'max_urls'       => 50,
            'notify_email'   => get_option( 'admin_email' ),
            'notify_on'      => 'always', // always, threshold, never
            'threshold'      => 70,
        ) );

        $max_urls   = isset( $cron_settings['max_urls'] ) ? absint( $cron_settings['max_urls'] ) : 50;
        $post_types = ! empty( $settings['post_types'] ) ? $settings['post_types'] : array( 'post', 'page' );

        // Collect URLs
        $urls = array();
        if ( ! empty( $settings['include_home'] ) ) {
            $urls[] = home_url( '/' );
        }

        $posts = get_posts( array(
            'post_type'      => $post_types,
            'post_status'    => 'publish',
            'posts_per_page' => $max_urls,
            'fields'         => 'ids',
        ) );

        foreach ( $posts as $post_id ) {
            $urls[] = get_permalink( $post_id );
        }

        $urls = array_slice( array_unique( $urls ), 0, $max_urls );

        if ( empty( $urls ) ) {
            $this->log( 'Nenhuma URL encontrada para análise cron.' );
            return;
        }

        $this->log( sprintf( 'Iniciando scan cron de %d URLs.', count( $urls ) ) );

        // Run server-side analysis
        $all_violations = array();
        $total_score    = 0;
        $url_count      = 0;

        foreach ( $urls as $url ) {
            $result = $this->analyze_url_serverside( $url );

            if ( $result && ! is_wp_error( $result ) ) {
                $all_violations = array_merge( $all_violations, $result['violations'] );
                $total_score   += $result['score'];
                $url_count++;
            }
        }

        $avg_score      = $url_count > 0 ? round( $total_score / $url_count ) : 0;
        $total_problems = array_sum( array_column( $all_violations, 'count' ) );

        // Save to history
        $report = array(
            'timestamp'      => current_time( 'mysql' ),
            'user_id'        => 0,
            'score'          => $avg_score,
            'total_problems' => $total_problems,
            'source'         => 'Cron',
            'url'            => home_url( '/' ),
            'urls_analyzed'  => $url_count,
            'summary'        => array(
                'score'         => $avg_score,
                'totalProblems' => $total_problems,
                'rulesViolated' => count( $all_violations ),
            ),
            'violations'     => $all_violations,
        );

        // Save in DB
        $db = Wally_Database::get_instance();
        $db->insert_report( home_url( '/' ), $report, $avg_score, $total_problems );

        // Update history option
        $history = get_option( 'wally_monitor_history', array() );
        $cron_mode = 'wcag';
        $cron_settings_main = get_option( 'wally_monitor_settings', array() );
        if ( ! empty( $cron_settings_main['analysis_mode'] ) ) {
            $cron_mode = sanitize_key( $cron_settings_main['analysis_mode'] );
        }
        array_unshift( $history, array(
            'timestamp'      => current_time( 'mysql' ),
            'user_id'        => 0,
            'score'          => $avg_score,
            'total_problems' => $total_problems,
            'url'            => home_url( '/' ),
            'source'         => 'Cron',
            'analysis_mode'  => $cron_mode,
        ) );
        $history = array_slice( $history, 0, 100 );
        update_option( 'wally_monitor_history', $history, false );

        // Save last cron result summary
        update_option( 'wally_monitor_last_cron', array(
            'timestamp'  => current_time( 'mysql' ),
            'score'      => $avg_score,
            'problems'   => $total_problems,
            'urls'       => $url_count,
        ), false );

        $this->log( sprintf( 'Scan cron concluído. Score: %d/100, Problemas: %d, URLs: %d', $avg_score, $total_problems, $url_count ) );

        // Send notification
        $this->maybe_send_notification( $cron_settings, $avg_score, $total_problems, $url_count );
    }

    /**
     * Server-side URL analysis (regex-based).
     *
     * @param string $url URL to analyze.
     * @return array|WP_Error
     */
    private function analyze_url_serverside( $url ) {
        // SSRF protection — validate URL before fetching
        $url_check = wally_monitor_validate_url( $url );
        if ( is_wp_error( $url_check ) ) {
            return $url_check;
        }

        $response = wp_remote_get( $url, array(
            'timeout'   => 30,
            'sslverify' => apply_filters( 'wally_monitor_sslverify', true ),
        ) );

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

        $html = wp_remote_retrieve_body( $response );
        if ( empty( $html ) ) {
            return new WP_Error( 'empty', __( 'Resposta vazia', 'wally-monitor' ) );
        }

        $violations = array();
        $penalty    = 0;

        // DOMDocument-based accessibility checks (replaces regex)
        $checker = new Wally_HTML_Checker( $html );
        $result  = $checker->run_core_checks();

        $violations = $result['violations'];
        $penalty    = $result['penalty'];

        $score = Wally_HTML_Checker::calculate_score( $penalty );

        return array(
            'url'        => $url,
            'score'      => $score,
            'violations' => $violations,
        );
    }

    /**
     * Send email notification if configured.
     *
     * @param array $settings  Cron settings.
     * @param int   $score     Average score.
     * @param int   $problems  Total problems.
     * @param int   $url_count URLs analyzed.
     */
    private function maybe_send_notification( $settings, $score, $problems, $url_count ) {
        $notify_on = isset( $settings['notify_on'] ) ? $settings['notify_on'] : 'always';
        $threshold = isset( $settings['threshold'] ) ? absint( $settings['threshold'] ) : 70;
        $email     = isset( $settings['notify_email'] ) ? $settings['notify_email'] : get_option( 'admin_email' );

        if ( 'never' === $notify_on ) {
            return;
        }

        if ( 'threshold' === $notify_on && $score >= $threshold ) {
            return; // Only notify when below threshold
        }

        $site_name = get_bloginfo( 'name' );
        /* translators: %1$s: site name, %2$d: accessibility score */
        $subject   = sprintf( __( '[Equallyze] %1$s — Score: %2$d/100', 'wally-monitor' ), $site_name, $score );

        $score_emoji = $score >= 90 ? '✅' : ( $score >= 70 ? '⚠️' : '❌' );
        $score_label = $score >= 90 ? __( 'Excelente', 'wally-monitor' ) : ( $score >= 70 ? __( 'Bom', 'wally-monitor' ) : __( 'Precisa de atenção', 'wally-monitor' ) );

        /* translators: %1$s: score emoji, %2$d: score value, %3$s: score label, %4$d: problems count, %5$d: URLs analyzed count, %6$s: date, %7$s: admin URL */
        $body = sprintf(
            __( "Relatório Automático de Acessibilidade\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n%1\$s Score: %2\$d/100 (%3\$s)\nProblemas encontrados: %4\$d\nURLs analisadas: %5\$d\nData: %6\$s\n\nPara ver o relatório completo, acesse:\n%7\$s\n\n—\nEquallyze — Monitor de Acessibilidade\nhttps://equallyze.com", 'wally-monitor' ),
            $score_emoji,
            $score,
            $score_label,
            $problems,
            $url_count,
            current_time( 'mysql' ),
            admin_url( 'admin.php?page=wally-monitor' )
        );

        wp_mail( $email, $subject, $body );
    }

    /**
     * Register cron-related settings fields.
     */
    public function register_settings() {
        register_setting( 'wally_monitor_cron_group', 'wally_monitor_cron_settings', array(
            'type'              => 'array',
            'sanitize_callback' => array( $this, 'sanitize_cron_settings' ),
        ) );
    }

    /**
     * Sanitize cron settings.
     *
     * @param array $input Raw input.
     * @return array Sanitized settings.
     */
    public function sanitize_cron_settings( $input ) {
        return array(
            'max_urls'       => isset( $input['max_urls'] ) ? absint( $input['max_urls'] ) : 50,
            'notify_email'   => isset( $input['notify_email'] ) ? sanitize_email( $input['notify_email'] ) : get_option( 'admin_email' ),
            'notify_on'      => isset( $input['notify_on'] ) && in_array( $input['notify_on'], array( 'always', 'threshold', 'never' ), true ) ? $input['notify_on'] : 'always',
            'threshold'      => isset( $input['threshold'] ) ? max( 0, min( 100, absint( $input['threshold'] ) ) ) : 70,
        );
    }

    /**
     * Log cron activity.
     *
     * @param string $message Log message.
     */
    private function log( $message ) {
        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
            error_log( '[Wally Monitor] ' . $message );
        }
    }

    /**
     * Map frequency string to WordPress cron recurrence.
     *
     * @param string $frequency Frequency name.
     * @return string WP cron recurrence.
     */
    private static function map_frequency( $frequency ) {
        $map = array(
            'hourly'  => 'hourly',
            'daily'   => 'daily',
            'weekly'  => 'weekly',
            'monthly' => self::MONTHLY_RECURRENCE,
        );
        return isset( $map[ $frequency ] ) ? $map[ $frequency ] : 'weekly';
    }

    /**
     * Cleanup on plugin deactivation.
     */
    public static function deactivate() {
        self::disable();
    }
}
