<?php
/**
 * Incident Response module — Post-Incident Forensics & Lockdown.
 *
 * Phase 0 (scaffold): module skeleton, opt-in setting (default OFF), the
 * wp_shieldforge_incidents table, and a capability + nonce-gated manual
 * "emergency audit" trigger (stub — the forensic triage engine lands in a
 * later phase). Nothing here changes site behaviour until later phases.
 *
 * Design principles: docs/incident-module/01-design-principles.md
 * Roadmap slot: Phase 7 Stage 3 (Incident Commander / Responder layer).
 *
 * @package Brainwerk Security Suite
 */

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

class Shieldforge_Incident {

    /** Background sweep cron hook (engine wired in a later phase). */
    const CRON_HOOK = 'shieldforge_incident_sweep';

    /** admin-post.php action for the manual "emergency audit" button. */
    const AUDIT_ACTION = 'shieldforge_incident_audit';

    /** Nonce action for the manual audit trigger. */
    const AUDIT_NONCE = 'shieldforge_incident_audit';

    /** One-shot admin-notice transient prefix (per user). */
    const NOTICE_TRANSIENT = 'shieldforge_incident_notice';

    public static function register( Shieldforge_Loader $loader ) {
        // The manual trigger is ALWAYS available to capable admins — an admin
        // explicitly asking for an audit is never "off". Automatic triggers
        // (a later phase) will be gated behind the incident_enabled opt-in.
        $loader->add_action( 'admin_post_' . self::AUDIT_ACTION, __CLASS__, 'handle_manual_audit' );
        $loader->add_action( 'admin_notices', __CLASS__, 'maybe_render_notice' );
    }

    /** Whether automatic detection/response is opted in (default OFF). */
    public static function is_enabled() {
        return (bool) Shieldforge_Settings::get( 'incident_enabled', false );
    }

    /* ------------------------------------------------------------------ *
     * Schema (DRY: used by the activator AND the v4 migration)
     * ------------------------------------------------------------------ */

    public static function table_sql() {
        global $wpdb;
        $table           = Shieldforge_Utils::table( 'incidents' );
        $charset_collate = $wpdb->get_charset_collate();
        return "CREATE TABLE {$table} (
            id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
            blog_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
            status VARCHAR(20) NOT NULL DEFAULT 'open',
            confidence VARCHAR(20) NOT NULL DEFAULT 'suspected',
            severity TINYINT UNSIGNED NOT NULL DEFAULT 2,
            trigger_type VARCHAR(64) DEFAULT NULL,
            lockdown_stage TINYINT UNSIGNED NOT NULL DEFAULT 0,
            summary VARCHAR(255) DEFAULT NULL,
            indicators LONGTEXT,
            evidence_ref VARCHAR(255) DEFAULT NULL,
            created_at DATETIME NOT NULL,
            updated_at DATETIME NOT NULL,
            PRIMARY KEY  (id),
            KEY idx_blog (blog_id),
            KEY idx_status (status),
            KEY idx_confidence (confidence),
            KEY idx_created (created_at)
        ) {$charset_collate};";
    }

    public static function create_table() {
        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        dbDelta( self::table_sql() );
    }

    /* ------------------------------------------------------------------ *
     * Read API (for the admin view)
     * ------------------------------------------------------------------ */

    public static function recent_incidents( $limit = 20 ) {
        global $wpdb;
        $table = Shieldforge_Utils::table( 'incidents' );
        $limit = max( 1, (int) $limit );
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- custom security table; direct live read by design; not cacheable.
        return $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table} ORDER BY created_at DESC LIMIT %d", $limit ) );
    }

    public static function count_open() {
        global $wpdb;
        $table = Shieldforge_Utils::table( 'incidents' );
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- custom security table; direct live read by design; not cacheable.
        return (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$table} WHERE status NOT IN ('resolved','false_positive')" );
    }

    /* ------------------------------------------------------------------ *
     * Manual trigger (Phase 0 STUB — no scan/lockdown yet)
     * ------------------------------------------------------------------ */

    public static function handle_manual_audit() {
        if ( ! current_user_can( Shieldforge_Admin::CAP ) && ! current_user_can( 'manage_network_options' ) ) {
            wp_die(
                esc_html__( 'You do not have permission to do this.', 'brainwerk-security-suite' ),
                '',
                array( 'response' => 403 )
            );
        }
        check_admin_referer( self::AUDIT_NONCE );

        // Phase 0: scaffold only. The forensic triage engine (read-only,
        // chunked/async) is added in a later phase. We log the intent and
        // hand back a notice so the wiring (capability + nonce + redirect +
        // notice) is verifiable now. NOTHING is scanned, changed or locked.
        if ( class_exists( 'Shieldforge_Logger' ) ) {
            Shieldforge_Logger::log( 'incident_audit_requested', array(
                'severity' => Shieldforge_Logger::SEV_INFO,
                'context'  => array( 'source' => 'manual', 'phase' => 'scaffold' ),
            ) );
        }

        set_transient(
            self::NOTICE_TRANSIENT . '_' . get_current_user_id(),
            __( 'Notfall-Audit angefordert. Das Modul-Gerüst ist aktiv — die Forensik-/Triage-Engine wird in einer späteren Phase ergänzt (es wurde nichts gescannt oder verändert).', 'brainwerk-security-suite' ),
            60
        );

        $redirect = class_exists( 'Shieldforge_Admin' )
            ? Shieldforge_Admin::tab_url( 'incident' )
            : admin_url( 'admin.php?page=brainwerk-security-suite&tab=incident' );
        wp_safe_redirect( $redirect );
        exit;
    }

    public static function maybe_render_notice() {
        $key    = self::NOTICE_TRANSIENT . '_' . get_current_user_id();
        $notice = get_transient( $key );
        if ( ! $notice ) {
            return;
        }
        delete_transient( $key );
        echo '<div class="notice notice-info is-dismissible"><p>' . esc_html( $notice ) . '</p></div>';
    }
}
