<?php

namespace UltraguardPro\modules\authenticator;

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

use UltraguardPro\Plugin;
use UltraguardPro\modules\core\AbstractModule;
use WP_User;

class TwoFactorModule extends AbstractModule {

    private const TRANSIENT_PREFIX = 'ultraguard_2fa_pending_';

    public function __construct( Plugin $plugin ) {
        parent::__construct( $plugin );
        $this->plugin->settings->register_module_prefix( 'two_factor_', 'two_factor' );
    }

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

    protected function requires_pro(): bool {
        return false;
    }

    public function boot(): void {
        $_db_tables = $this->plugin->modules->get_db_tables();
        if ( $_db_tables ) {
            $_db_tables->register_schema( new schema\TwoFactorSchema() );
        }

        if ( method_exists( $this->plugin->settings, 'register_settings_schema' ) ) {
            $this->plugin->settings->register_settings_schema( 'two_factor', 'two_factor_', [
                'two_factor_enabled'              => [ 'type' => 'bool', 'default' => true ],
                'two_factor_allow_self_enroll'   => [ 'type' => 'bool', 'default' => true ],
                'two_factor_required_roles'      => [ 'type' => 'array', 'default' => [], 'options' => array_keys( wp_roles()->get_names() ) ],
                'two_factor_grace_period_days'   => [ 'type' => 'int', 'default' => 7 ],
                'two_factor_show_setup_prompt'   => [ 'type' => 'bool', 'default' => true ],
                'two_factor_backup_code_count'   => [ 'type' => 'select', 'default' => '8', 'options' => [ '6', '8', '10' ] ],
                'two_factor_email_notifications' => [ 'type' => 'bool', 'default' => true ],
                'two_factor_issuer_name'         => [ 'type' => 'text', 'default' => get_bloginfo( 'name' ) ],
                'two_factor_scheduled_task'      => [ 'type' => 'bool', 'default' => false ],
                'two_factor_task_frequency'      => [ 'type' => 'select', 'default' => 'daily', 'options' => [ 'hourly', 'twicedaily', 'daily' ] ],
            ] );
        }

        ( new ajax\TwoFactorAjax( $this->plugin ) )->register();
        ( new TwoFactorDashboardContributor( $this->plugin ) )->register();

        add_action( 'ultraguard_register_admin_pages', [ $this, 'register_pages' ], 56, 1 );
        add_action( 'ultraguard_two_factor_cron_task', [ $this, 'run_cron_task' ] );
        add_action( 'wp_login', [ $this, 'intercept_login_flow' ], 10, 2 );
        add_action( 'login_form', [ $this, 'inject_login_form_notice' ] );
        add_action( 'login_form_ultraguard_2fa_verify', [ $this, 'render_verification_ui' ] );
        add_filter( 'authenticate', [ $this, 'validate_pending_two_factor' ], 95, 3 );
        add_action( 'show_user_profile', [ $this, 'render_profile_controls' ] );
        add_action( 'edit_user_profile', [ $this, 'render_profile_controls' ] );
        add_action( 'personal_options_update', [ $this, 'save_profile_preferences' ] );
        add_action( 'edit_user_profile_update', [ $this, 'save_profile_preferences' ] );

        ( new TwoFactorNotifications( $this->plugin ) )->register();

        add_action( 'ultraguard_after_enqueue_assets', [ $this, 'enqueue_assets' ] );
        add_action( 'wp_loaded', [ $this, 'maybe_setup_schedule' ] );
        add_action( 'update_option_two_factor_task_frequency', [ $this, 'reschedule_cron' ], 10, 2 );

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

    public function register_pages( $admin ): void {
        $admin->register_page( [
            'id'              => 'two-factor',
            'page_title'      => __( 'Authentication', 'ultraguard-security-suite' ),
            'menu_title'      => __( 'Authentication', 'ultraguard-security-suite' ),
            'capability'      => 'manage_options',
            'scheme_callback' => [ new pages\TwoFactorDashboard( $this->plugin ), 'get_scheme' ],
            'parent_id'       => '',
            'priority'        => 56,
            'group'           => 'Access Control',
            'icon'            => 'dashicons-shield-alt',
        ] );

        $admin->register_page( [
            'id'              => 'two-factor-settings',
            'page_title'      => __( 'Two-Factor Settings', 'ultraguard-security-suite' ),
            'menu_title'      => __( 'Two-Factor Settings', 'ultraguard-security-suite' ),
            'capability'      => 'manage_options',
            'scheme_callback' => [ new pages\TwoFactorSettings( $this->plugin ), 'get_scheme' ],
            'parent_id'       => 'two-factor',
            'priority'        => 57,
            'group'           => 'Access Control',
            'icon'            => 'dashicons-admin-settings',
        ] );
    }

    public function intercept_login_flow( string $user_login, WP_User $user ): void {
        if ( ! $this->should_require_two_factor( $user ) ) {
            return;
        }

        wp_clear_auth_cookie();

        $token = wp_generate_password( 32, false, false );
        set_transient( self::TRANSIENT_PREFIX . $token, [
            'user_id'    => (int) $user->ID,
            'user_login' => $user_login,
            'remember'   => ! empty( $_REQUEST['rememberme'] ), // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only login form state carried into the pending session.
            'created_at' => time(),
        ], 10 * MINUTE_IN_SECONDS );

        wp_safe_redirect( add_query_arg( [
            'action' => 'ultraguard_2fa_verify',
            'token'  => rawurlencode( $token ),
        ], wp_login_url() ) );
        exit;
    }

    public function inject_login_form_notice(): void {
        if ( 'ultraguard_2fa_verify' !== sanitize_key( wp_unslash( $_GET['action'] ?? '' ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only login screen detection.
            return;
        }

        echo '<p class="message">' . esc_html__( 'Complete login with your authenticator app or a backup code.', 'ultraguard-security-suite' ) . '</p>';
    }

    public function render_verification_ui(): void {
        $token   = sanitize_text_field( wp_unslash( $_GET['token'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Token is validated via pending-session lookup, not used for mutation.
        $pending = $this->get_pending_session( $token );

        if ( ! $pending ) {
            wp_die( esc_html__( 'Your two-factor verification session has expired. Please sign in again.', 'ultraguard-security-suite' ), esc_html__( 'Verification expired', 'ultraguard-security-suite' ), [ 'response' => 403 ] );
        }

        login_header( __( 'Two-Factor Verification', 'ultraguard-security-suite' ), '<p>' . esc_html__( 'Enter the 6-digit code from your authenticator app or use one of your backup codes.', 'ultraguard-security-suite' ) . '</p>' );
        ?>
<form method="post" action="<?php echo esc_url( self_admin_url( 'admin-ajax.php' ) ); ?>">
            <input type="hidden" name="action" value="ultraguard_2fa_verify_login">
            <input type="hidden" name="nonce" value="<?php echo esc_attr( wp_create_nonce( 'ultraguard_admin_nonce' ) ); ?>">
            <input type="hidden" name="token" value="<?php echo esc_attr( $token ); ?>">
            <p><label for="ultraguard_2fa_code"><?php esc_html_e( 'Authentication code', 'ultraguard-security-suite' ); ?></label><input type="text" name="code" id="ultraguard_2fa_code" class="input" inputmode="numeric" autocomplete="one-time-code"></p>
            <p class="submit"><button type="submit" class="button button-primary button-large"><?php esc_html_e( 'Verify and sign in', 'ultraguard-security-suite' ); ?></button></p>
        </form>
<form method="post" action="<?php echo esc_url( self_admin_url( 'admin-ajax.php' ) ); ?>">
            <input type="hidden" name="action" value="ultraguard_2fa_verify_backup">
            <input type="hidden" name="nonce" value="<?php echo esc_attr( wp_create_nonce( 'ultraguard_admin_nonce' ) ); ?>">
            <input type="hidden" name="token" value="<?php echo esc_attr( $token ); ?>">
            <p><label for="ultraguard_2fa_backup"><?php esc_html_e( 'Backup code', 'ultraguard-security-suite' ); ?></label><input type="text" name="code" id="ultraguard_2fa_backup" class="input"></p>
            <p class="submit"><button type="submit" class="button button-secondary button-large"><?php esc_html_e( 'Use backup code', 'ultraguard-security-suite' ); ?></button></p>
        </form>
        <?php
        login_footer();
        exit;
    }

    public function validate_pending_two_factor( $user, string $username, string $password ) {
        return $user;
    }

    public function render_profile_controls( $user ): void {
        $table = $this->get_table( 'totp_credentials' );
        $row   = $table && method_exists( $table, 'get_for_user' ) ? $table->get_for_user( (int) $user->ID ) : [];
        ?>
        <h2><?php esc_html_e( 'UltraGuard Two-Factor Authentication', 'ultraguard-security-suite' ); ?></h2>
        <table class="form-table" role="presentation">
            <tr>
                <th><label for="ultraguard_two_factor_enabled"><?php esc_html_e( 'Enable 2FA', 'ultraguard-security-suite' ); ?></label></th>
                <td>
                    <input type="checkbox" name="ultraguard_two_factor_enabled" id="ultraguard_two_factor_enabled" value="1" <?php checked( ! empty( $row['is_enabled'] ) ); ?>>
                    <p class="description"><?php esc_html_e( 'This reflects whether TOTP is active for the user. Initial setup is done from the UltraGuard Two-Factor page.', 'ultraguard-security-suite' ); ?></p>
                </td>
            </tr>
        </table>
        <?php
    }

    public function save_profile_preferences( int $user_id ): void {
        if ( ! current_user_can( 'edit_user', $user_id ) ) {
            return;
        }

        $table = $this->get_table( 'totp_credentials' );
        if ( ! $table || ! method_exists( $table, 'set_enabled_flag' ) ) {
            return;
        }

        $enabled = ! empty( wp_unslash( $_POST['ultraguard_two_factor_enabled'] ?? '' ) ) ? 1 : 0; // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- User profile save is already nonce-protected by WordPress core; this is a boolean presence check only.
        $table->set_enabled_flag( $user_id, $enabled );
    }

    public function maybe_setup_schedule(): void {
        if ( $this->option_enabled( 'two_factor_scheduled_task' ) && ! wp_next_scheduled( 'ultraguard_two_factor_cron_task' ) ) {
            $freq = $this->option( 'two_factor_task_frequency', 'daily' );
            wp_schedule_event( time(), $freq, 'ultraguard_two_factor_cron_task' );
        }
    }

    public function reschedule_cron( $old, $new ): void {
        wp_clear_scheduled_hook( 'ultraguard_two_factor_cron_task' );
        $this->maybe_setup_schedule();
    }

    public function run_cron_task(): void {
        $table = $this->get_table( 'totp_credentials' );
        if ( $table && method_exists( $table, 'clear_expired_locks' ) ) {
            $table->clear_expired_locks();
        }
    }

    public function enqueue_assets(): void {
        $ug_page      = sanitize_key( wp_unslash( $_GET['ug_page'] ?? ''  ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only screen detection.
        $module_pages = [ 'authenticator', 'two-factor', 'two-factor-settings' ];

        if ( ! in_array( $ug_page, $module_pages, true ) ) {
            return;
        }

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

        wp_enqueue_style( 'ultraguard-two-factor-page', $module_url . 'assets/css/two-factor-page.css', [ 'admin-base', 'admin-components' ], $version );
        wp_enqueue_script( 'ultraguard-two-factor-page', $module_url . 'assets/js/two-factor-page.js', [ 'jquery', 'ultraguard-core', 'ultraguard-managers' ], $version, true );

        wp_localize_script( 'ultraguard-two-factor-page', 'ultraguardTwoFactor', [
            'nonce'   => wp_create_nonce( 'ultraguard_admin_nonce' ),
            'ajaxUrl' => self_admin_url( 'admin-ajax.php' ),
            'i18n'    => [
                'scanInstructions' => __( 'Scan this QR code with Google Authenticator, Authy, Microsoft Authenticator, or another compatible app.', 'ultraguard-security-suite' ),
                'verifyInstructions' => __( 'After scanning, enter the current 6-digit code shown in your authenticator app to confirm setup.', 'ultraguard-security-suite' ),
                'codeLabel'      => __( '6-digit authenticator code', 'ultraguard-security-suite' ),
                'qrAlt'          => __( 'Authenticator app QR code', 'ultraguard-security-suite' ),
                'openQr'         => __( 'Open QR code in a new tab', 'ultraguard-security-suite' ),
                'backupCodesTitle' => __( 'Backup codes', 'ultraguard-security-suite' ),
                'backupCodesHelp'  => __( 'Save these backup codes somewhere safe. Each code can be used once if you lose access to your authenticator app.', 'ultraguard-security-suite' ),
                'copied'         => __( 'Backup codes copied.', 'ultraguard-security-suite' ),
                'setupConfirmed' => __( 'Authenticator verified. Save to finish setup.', 'ultraguard-security-suite' ),
                'setupFailed'    => __( 'The code could not be verified. Please try again.', 'ultraguard-security-suite' ),
            ],
        ] );
    }

    public function should_require_two_factor( WP_User $user ): bool {
        if ( ! $this->option( 'two_factor_enabled', true ) ) {
            return false;
        }

        $table = $this->get_table( 'totp_credentials' );
        if ( ! $table || ! method_exists( $table, 'is_enabled_for_user' ) ) {
            return false;
        }

        if ( $table->is_enabled_for_user( (int) $user->ID ) ) {
            return true;
        }

        $required_roles = (array) $this->option( 'two_factor_required_roles', [] );
        if ( empty( $required_roles ) || ! array_intersect( $user->roles, $required_roles ) ) {
            return false;
        }

        $grace_days = (int) $this->option( 'two_factor_grace_period_days', 7 );
        if ( $user->user_registered && strtotime( $user->user_registered . ' +' . $grace_days . ' days' ) > time() ) {
            return false;
        }

        return true;
    }

    public function get_pending_session( string $token ): ?array {
        if ( '' === $token ) {
            return null;
        }

        $data = get_transient( self::TRANSIENT_PREFIX . $token );
        return is_array( $data ) ? $data : null;
    }

    public function consume_pending_session( string $token ): void {
        delete_transient( self::TRANSIENT_PREFIX . $token );
    }

    public function complete_pending_login( string $token, bool $remember = false ): bool {
        $pending = $this->get_pending_session( $token );
        if ( ! $pending ) {
            return false;
        }

        $user = get_userdata( (int) $pending['user_id'] );
        if ( ! $user ) {
            $this->consume_pending_session( $token );
            return false;
        }

        wp_set_current_user( $user->ID );
        wp_set_auth_cookie( $user->ID, $remember || ! empty( $pending['remember'] ) );
        do_action( 'wp_login', $user->user_login, $user );
        $this->consume_pending_session( $token );

        return true;
    }
}
