<?php
/**
 * Notifications — mail alerts on critical events + daily digest cron.
 *
 * @package Brainwerk Security Suite
 */

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

class Shieldforge_Notifications {

    public static function register( Shieldforge_Loader $loader ) {
        if ( ! Shieldforge_Settings::get( 'notify_email_enabled', true ) ) {
            // Still register digest if enabled — admin may want digest only.
        }
        $loader->add_action( 'shieldforge_bf_lockout',          __CLASS__, 'on_bf_lockout', 10, 3 );
        $loader->add_action( 'shieldforge_admin_user_created',  __CLASS__, 'on_admin_created', 10, 2 );
        $loader->add_action( 'shieldforge_probing_threshold',   __CLASS__, 'on_probing_threshold', 10, 2 );
        $loader->add_action( 'shieldforge_daily_digest',        __CLASS__, 'send_daily_digest' );
    }

    public static function on_bf_lockout( $ip, $count, $username ) {
        $subject = sprintf(
            /* translators: %s: site name */
            __( '[%s] Brainwerk Security Suite: brute-force lockout triggered', 'brainwerk-security-suite' ),
            self::site_name()
        );
        $body = sprintf(
            /* translators: 1: IP, 2: failure count, 3: username */
            __( 'A brute-force lockout was triggered.\n\nIP: %1$s\nFailed attempts: %2$d\nLast tried username: %3$s\n', 'brainwerk-security-suite' ),
            $ip,
            (int) $count,
            $username
        );
        self::send( $subject, $body, Shieldforge_Logger::SEV_HIGH );
    }

    public static function on_admin_created( $user_id, $role ) {
        $user = get_userdata( $user_id );
        $subject = sprintf( __( '[%s] Brainwerk Security Suite: new administrator created', 'brainwerk-security-suite' ), self::site_name() );
        $body = sprintf(
            __( "A new user with role '%1\$s' was created.\n\nUser: %2\$s (ID %3\$d)\nEmail: %4\$s\nCreated by: %5\$s\n", 'brainwerk-security-suite' ),
            $role,
            $user ? $user->user_login : '',
            (int) $user_id,
            $user ? $user->user_email : '',
            wp_get_current_user()->user_login ?: 'unknown'
        );
        self::send( $subject, $body, Shieldforge_Logger::SEV_CRITICAL );
    }

    public static function on_probing_threshold( $ip, $count ) {
        $subject = sprintf( __( '[%s] Brainwerk Security Suite: probing burst detected', 'brainwerk-security-suite' ), self::site_name() );
        $body = sprintf(
            __( "An IP triggered the probing alert threshold.\n\nIP: %1\$s\nProbing 404s in last hour: %2\$d\n", 'brainwerk-security-suite' ),
            $ip,
            (int) $count
        );
        self::send( $subject, $body, Shieldforge_Logger::SEV_HIGH );
    }

    /**
     * Centralised mail send — checks toggle, recipient, quiet hours.
     * Critical events bypass quiet hours.
     */
    public static function send( $subject, $body, $severity = Shieldforge_Logger::SEV_INFO ) {
        if ( ! Shieldforge_Settings::get( 'notify_email_enabled', true ) ) {
            return false;
        }
        $to = Shieldforge_Settings::get( 'notify_email_to', get_option( 'admin_email' ) );
        if ( ! is_email( $to ) ) {
            return false;
        }
        if ( $severity < Shieldforge_Logger::SEV_CRITICAL && self::in_quiet_hours() ) {
            return false;
        }
        return wp_mail( $to, $subject, $body );
    }

    public static function in_quiet_hours() {
        $hours = Shieldforge_Settings::get( 'notify_quiet_hours', array( 'start' => '22:00', 'end' => '07:00' ) );
        $now   = (int) wp_date( 'Hi' );
        $start = (int) str_replace( ':', '', $hours['start'] );
        $end   = (int) str_replace( ':', '', $hours['end'] );
        if ( $start === $end ) {
            return false;
        }
        if ( $start < $end ) {
            return $now >= $start && $now < $end;
        }
        // Wraps midnight (e.g. 22:00 -> 07:00)
        return $now >= $start || $now < $end;
    }

    public static function send_daily_digest() {
        if ( ! Shieldforge_Settings::get( 'digest_enabled', true ) ) {
            return false;
        }
        $to = Shieldforge_Settings::get( 'notify_email_to', get_option( 'admin_email' ) );
        if ( ! is_email( $to ) ) {
            return false;
        }

        $data    = Shieldforge_Threat_Intel::build_digest();
        $body    = Shieldforge_Threat_Intel::format_digest_html( $data, self::site_name() );
        $score   = $data['score'];
        // Subject is pure ASCII so it never needs RFC 2047 encoding —
        // works whether the mail goes through php mail(), classic SMTP,
        // OAuth-SMTP plugins (FluentSMTP, WP Mail SMTP, Post SMTP), or a
        // Microsoft Graph / Office365 wrapper.
        $subject = sprintf(
            /* translators: 1: site name, 2: threat label, 3: numeric score */
            __( '[%1$s] Brainwerk Security Suite digest - %2$s threat (%3$d/100)', 'brainwerk-security-suite' ),
            self::site_name(),
            strtoupper( $score['label'] ),
            (int) $score['score']
        );

        // Force HTML content type for THIS send so the structured tables render
        // in every client. Filters are removed right after the call so they
        // can't leak into other plugins' wp_mail() invocations in the same
        // request. (The plain-text builder format_digest_text() is kept for
        // callers/integrations that want an ASCII rendering.)
        $force_html = function ( $type ) {
            return 'text/html';
        };
        $force_charset = function ( $charset ) {
            return 'UTF-8';
        };
        add_filter( 'wp_mail_content_type', $force_html );
        add_filter( 'wp_mail_charset', $force_charset );
        $sent = wp_mail( $to, $subject, $body );
        remove_filter( 'wp_mail_content_type', $force_html );
        remove_filter( 'wp_mail_charset', $force_charset );

        if ( ! $sent ) {
            // Defensive logging — on hosts where wp_mail() silently fails
            // (no MTA, OAuth not configured, rate limit), this leaves a
            // visible trail in the Brainwerk Security Suite log so the admin can spot
            // it without digging through the WP debug log.
            Shieldforge_Logger::log( 'digest_send_failed', array(
                'severity' => Shieldforge_Logger::SEV_WARN,
                'context'  => array(
                    'recipient'     => $to,
                    'threat_score'  => (int) $score['score'],
                    'threat_label'  => (string) $score['label'],
                ),
            ) );
        }

        return $sent;
    }

    private static function site_name() {
        return wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
    }
}
