<?php
/**
 * FilecheckerModule — Pro-tier File Integrity Monitoring feature module.
 *
 * Responsibilities:
 *   • Owns 3 DB tables  : file_integrity, scan_sessions, fc_quarantine
 *   • 6 admin pages     : dashboard, monitoring, changes, quarantine, history, settings
 *   • AJAX handlers     : registered via FilecheckerAjax (direct wp_ajax hooks)
 *   • Cron hooks        : full scan, quick scan, post-upgrade scan
 *   • Dashboard contrib : stat-card, status item, coverage bar, tile
 *
 * Architecture rules:
 *   • Requires Pro license (requires_pro() → true).
 *   • All scan logic is delegated to FilecheckerEngine — the module only
 *     wires hooks and owns the lifecycle.
 *   • Cross-module data uses apply_filters / do_action only.
 *   • No raw $wpdb queries — all DB access via table objects.
 *
 * @package UltraguardPro\modules\filechecker
 * @since   6.5.0
 */

namespace UltraguardPro\modules\filechecker;

use UltraguardPro\modules\core\AbstractModule;
use UltraguardPro\support\AjaxResponseBuilder;
use UltraguardPro\support\LicenseInfo;

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

class FilecheckerModule extends AbstractModule {

    // ── License ────────────────────────────────────────────────────────────────

    /** This module requires a Pro license. */
    protected function requires_pro(): bool { return true; }

    protected function allow_preview_mode(): bool { return true; }

    public function get_id(): string { return 'filechecker'; }

    /**
     * Register our settings prefix immediately so Settings::get_option() can
     * resolve 'filechecker_*' keys before boot() is called.
     */
    public function __construct( \UltraguardPro\Plugin $plugin ) {
        parent::__construct( $plugin );
        $this->plugin->settings->register_module_prefix( 'filechecker_', 'filechecker' );
    }

    // ── Boot ──────────────────────────────────────────────────────────────────

    /**
     * Bootstrap the module: schema → AJAX → pages → runtime hooks.
     * Called once by ModuleRegistry when is_enabled() === true.
     */
    public function boot(): void {
        // 1. Register DB schema (3 tables: file_integrity, scan_sessions, fc_quarantine)
        $_db_tables = $this->plugin->modules->get_db_tables();
        if ( $_db_tables ) {
            $_db_tables->register_schema( new schema\FilecheckerSchema() );
        }

        // 2. Register AJAX handlers (handles nonce + cap + license gates internally)
        ( new ajax\FilecheckerAjax( $this->plugin ) )->register();

        // 3. Dashboard stat-card / coverage bar contributor
        ( new FilecheckerDashboardContributor( $this->plugin ) )->register();

        // 4. Admin pages
        add_action( 'ultraguard_register_admin_pages', [ $this, 'register_pages' ], 25, 1 );

        // Free users still get the dashboard preview, but not the live runtime hooks.
        if ( $this->is_preview_mode() ) {
            do_action( 'ultraguard_pro/filechecker/preview_booted', $this );
            return;
        }

        // 5. WP Cron hooks
        add_action( 'ultraguard_filechecker_run',       [ $this, 'run_full_scan' ] );
        add_action( 'ultraguard_filechecker_quick_run', [ $this, 'run_quick_scan' ] );

        // 5b. Enqueue filechecker frontend assets + register realtime fallback AJAX
        // Use ultraguard_after_enqueue_assets (not admin_enqueue_scripts) so that
        // the core ultraguard-managers and ultraguard-realtime handles are already
        // registered before we declare them as dependencies.
        add_action( 'ultraguard_after_enqueue_assets', [ $this, 'enqueue_assets' ] );
        add_action( 'wp_ajax_ultraguard_rt_filechecker_progress', [ $this, 'handle_rt_filechecker_progress' ] );

        // 6. Re-scan after WP/plugin upgrades (delayed to avoid lock contention)
        add_action( 'upgrader_process_complete', [ $this, 'on_upgrade' ], 10, 2 );

        // 7. Register/reschedule the recurring cron event if needed
        add_action( 'init', [ $this, 'maybe_schedule_cron' ], 30 );

        do_action( 'ultraguard_pro/filechecker/booted', $this );
    }

    // ── Admin pages ────────────────────────────────────────────────────────────

    /**
     * Register all six admin pages for this module.
     *
     * @param object $admin  AdminManager instance.
     */
    public function register_pages( $admin ): void {

        // Parent page — File Integrity Checker dashboard
        $admin->register_page( [
            'id'              => 'filechecker',
            'page_title'      => __( 'File Integrity Checker', 'ultraguard-security-suite' ),
            'menu_title'      => __( 'File Checker',           'ultraguard-security-suite' ),
            'capability'      => 'manage_options',
            'scheme_callback' => [ pages\FilecheckerDashboard::class, $this->plugin ],
            'is_parent'       => true,
            'priority'        => 25,
            'group'           => 'Detection',
            'icon'            => 'dashicons-media-code',
        ] );

        $subpages = [
            [
                'id'       => 'filechecker-monitoring',
                'title'    => __( 'File Monitoring',         'ultraguard-security-suite' ),
                'menu'     => __( 'File Monitoring',         'ultraguard-security-suite' ),
                'class'    => pages\FilecheckerMonitoring::class,
                'priority' => 26,
                'icon'     => 'dashicons-visibility',
            ],
            [
                'id'       => 'filechecker-changes',
                'title'    => __( 'File Changes',            'ultraguard-security-suite' ),
                'menu'     => __( 'File Changes',            'ultraguard-security-suite' ),
                'class'    => pages\FilecheckerChanges::class,
                'priority' => 27,
                'icon'     => 'dashicons-update',
            ],
            [
                'id'       => 'filechecker-quarantine',
                'title'    => __( 'Quarantine',              'ultraguard-security-suite' ),
                'menu'     => __( 'Quarantine',              'ultraguard-security-suite' ),
                'class'    => pages\FilecheckerQuarantine::class,
                'priority' => 28,
                'icon'     => 'dashicons-lock',
            ],
            [
                'id'       => 'filechecker-history',
                'title'    => __( 'Scan History',            'ultraguard-security-suite' ),
                'menu'     => __( 'Scan History',            'ultraguard-security-suite' ),
                'class'    => pages\FilecheckerHistory::class,
                'priority' => 29,
                'icon'     => 'dashicons-backup',
            ],
            [
                'id'       => 'filechecker-settings',
                'title'    => __( 'File Checker Settings',   'ultraguard-security-suite' ),
                'menu'     => __( 'Settings',                'ultraguard-security-suite' ),
                'class'    => pages\FilecheckerSettings::class,
                'priority' => 30,
                'icon'     => 'dashicons-admin-settings',
            ],
        ];

        foreach ( $subpages as $page ) {
            $admin->register_page( [
                'id'              => $page['id'],
                'page_title'      => $page['title'],
                'menu_title'      => $page['menu'],
                'capability'      => 'manage_options',
                'scheme_callback' => [ $page['class'], $this->plugin ],
                'parent_id'       => 'filechecker',
                'priority'        => $page['priority'],
            'group'           => 'Detection',
                'icon'            => $page['icon'] ?? '',
            ] );
        }
    }

    // ── Cron scheduling ───────────────────────────────────────────────────────

    /**
     * Register or reschedule the recurring cron event based on saved settings.
     * Runs on `init` so settings are already loaded.
     */
    public function maybe_schedule_cron(): void {
        if ( ! $this->option( 'filechecker_scheduled', true ) ) {
            wp_clear_scheduled_hook( 'ultraguard_filechecker_run' );
            return;
        }

        $recurrence = $this->option( 'filechecker_schedule', 'daily' );
        $hook        = 'ultraguard_filechecker_run';

        $next = wp_next_scheduled( $hook );
        if ( ! $next ) {
            wp_schedule_event( time() + 60, $recurrence, $hook );
            return;
        }

        // Reschedule if frequency changed
        $schedules   = wp_get_schedules();
        $interval    = $schedules[ $recurrence ]['interval'] ?? 86400;
        $current_rec = $this->get_current_schedule_recurrence( $hook );

        if ( $current_rec !== $recurrence ) {
            wp_clear_scheduled_hook( $hook );
            wp_schedule_event( time() + 60, $recurrence, $hook );
        }
    }

    /**
     * Detect the recurrence label for an existing scheduled event.
     *
     * @param  string $hook  WP cron hook name.
     * @return string        Recurrence slug or empty string.
     */
    private function get_current_schedule_recurrence( string $hook ): string {
        $crons = _get_cron_array();
        foreach ( (array) $crons as $timestamp => $hooks ) {
            if ( isset( $hooks[ $hook ] ) ) {
                foreach ( $hooks[ $hook ] as $event ) {
                    return $event['schedule'] ?? '';
                }
            }
        }
        return '';
    }

    // ── Scan entry points (called by WP Cron) ─────────────────────────────────

    /**
     * Full integrity scan: all configured directories.
     * Delegates all logic to FilecheckerEngine.
     */
    public function run_full_scan(): void {
        $this->get_engine()->run( 'full' );
    }

    /**
     * Quick scan: WordPress core only.
     */
    public function run_quick_scan(): void {
        $this->get_engine()->run( 'quick' );
    }

    /**
     * Post-upgrade hook: schedule a full scan 60 s after update completes.
     *
     * @param \WP_Upgrader $upgrader   WordPress upgrader instance.
     * @param array        $hook_extra Extra data from the upgrader hook.
     */
    public function on_upgrade( \WP_Upgrader $upgrader, array $hook_extra ): void {
        if ( $this->option( 'filechecker_scan_on_update', true ) ) {
            wp_schedule_single_event( time() + 60, 'ultraguard_filechecker_run' );
        }
    }

    // ── Internal factory helpers ───────────────────────────────────────────────

    /**
     * Lazily create and return the FilecheckerEngine instance.
     *
     * @return FilecheckerEngine
     */
    public function get_engine(): FilecheckerEngine {
        static $engine = null;
        if ( null === $engine ) {
            $engine = new FilecheckerEngine( $this->plugin );
        }
        return $engine;
    }

    // ── Assets ────────────────────────────────────────────────────────────────

    /**
     * Enqueue the filechecker page JS on all admin pages.
     * The JS self-limits to filechecker pages via a class/id selector check.
     */
    public function enqueue_assets(): void {
        // Only load on filechecker pages — mirrors the pattern used by AntivirusModule.
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin screen detection.
        $ug_page   = sanitize_key( wp_unslash( $_GET['ug_page'] ?? ''  ) );
        $fc_pages  = [
            'filechecker', 'filechecker-monitoring', 'filechecker-changes',
            'filechecker-quarantine', 'filechecker-history', 'filechecker-settings',
        ];
        if ( ! in_array( $ug_page, $fc_pages, true ) ) {
            return;
        }

        $module_url = plugin_dir_url( __FILE__ );
        $version    = defined( 'ULTRAGUARD_PRO_VERSION' ) ? ULTRAGUARD_PRO_VERSION : '1.0.0';

        wp_enqueue_script(
            'ultraguard-filechecker-page',
            $module_url . 'assets/js/filechecker-page.js',
            [ 'jquery', 'ultraguard-managers', 'ultraguard-realtime' ],
            $version,
            true
        );

        // Pass the nonce and i18n strings into the JS
        $nonce = wp_create_nonce( 'ultraguard_admin_nonce' );
        wp_localize_script(
            'ultraguard-filechecker-page',
            'ultraguardFcData',
            [
                'nonce'  => $nonce,
                'i18n'   => [
                    'running'        => __( 'Runningâ¦',                             'ultraguard-security-suite' ),
                    'scanning'       => __( 'Scanningâ¦',                            'ultraguard-security-suite' ),
                    'idle'           => __( 'Idle',                                            'ultraguard-security-suite' ),
                    'complete'       => __( 'Complete',                                        'ultraguard-security-suite' ),
                    'cancelled'      => __( 'Cancelled',                                       'ultraguard-security-suite' ),
                    'error'          => __( 'Error',                                           'ultraguard-security-suite' ),
                    'scanComplete'   => __( 'Scan complete â results updated.',     'ultraguard-security-suite' ),
                    'scanCancelled'  => __( 'Scan cancelled.',                                 'ultraguard-security-suite' ),
                    'lostConnection' => __( 'Lost connection to scan. Please refresh the page.', 'ultraguard-security-suite' ),
                ],
            ]
        );
    }

    // ── Realtime fallback AJAX ─────────────────────────────────────────────────

    /**
     * Fallback AJAX handler for the 'filechecker_progress' realtime channel.
     * Used by ultraguard-realtime.js when SSE is unavailable.
     *
     * Returns the same data shape as handle_get_scan_progress() so the JS
     * renderer can handle both code paths identically.
     */
    public function handle_rt_filechecker_progress(): void {
        if ( ! check_ajax_referer( 'ultraguard_admin_nonce', 'nonce', false ) ) {
            AjaxResponseBuilder::make()
                ->nonceError( __( 'Security check failed.', 'ultraguard-security-suite' ) )
                ->send( 403 );
        }

        if ( ! \UltraguardPro\ultraguard_current_user_can_manage() ) {
            AjaxResponseBuilder::make()
                ->permissionError( __( 'Permission denied.', 'ultraguard-security-suite' ) )
                ->send( 403 );
            return;
        }

        $progress = get_transient( 'ultraguard_fc_scan_in_progress' );

        if ( $progress ) {
            AjaxResponseBuilder::make()
                ->success()
                ->mergeData( array_merge( $progress, [
                    'is_scanning' => true,
                ] ) )
                ->send();
        } else {
            AjaxResponseBuilder::make()
                ->success()
                ->mergeData( [
                    'is_scanning'     => false,
                    'percent'         => 100,
                    'current_file'    => '',
                    'changes_found'   => 0,
                    'files_processed' => 0,
                ] )
                ->send();
        }
    }

}
