<?php

/**
 * The public-facing functionality of the plugin.
 *
 * @link       https://kaizencoders.com
 * @since      1.0.0
 *
 * @package    Plugin_Name
 * @subpackage Plugin_Name/Frontend
 */

namespace KaizenCoders\MagicLink;

/**
 * The public-facing functionality of the plugin.
 *
 * Defines the plugin name, version, and two examples hooks for how to
 * enqueue the dashboard-specific stylesheet and JavaScript.
 *
 * @package    MagicLink
 * @author     KaizenCoders <hello@kaizencoders.com>
 */
class Frontend {
    /**
     * The plugin's instance.
     *
     * @since  1.0.0
     * @access private
     * @var    Plugin $plugin This plugin's instance.
     */
    private $plugin;

    /**
     * Initialize the class and set its properties.
     *
     * @param  Plugin  $plugin  This plugin's instance.
     *
     * @since 1.0.0
     *
     */
    public function __construct( Plugin $plugin ) {
        $this->plugin = $plugin;
    }

    public function handle_magic_link_login() {
        if ( ! isset( $_GET['magic_link'] ) || ! isset( $_GET['magic_token'] ) ) {
            return;
        }

        if ( IntentConfirmation::is_enabled() && ! IntentConfirmation::is_confirmation_submitted() ) {
            if ( ScannerDetector::is_current_request_scanner() ) {
                echo '<!doctype html><meta name="robots" content="noindex"><title>OK</title>';
                exit;
            }

            $query = array(
                'magic_link'  => 1,
                'magic_token' => sanitize_text_field( wp_unslash( $_GET['magic_token'] ) ),
            );
            echo IntentConfirmation::render( $query ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
            exit;
        }

        do_action( 'kc_ml_handle_login_request' );

        $token = sanitize_text_field( wp_unslash( $_GET['magic_token'] ) );

        $gmt_now = gmdate( 'Y-m-d H:i:s' );

        global $wpdb;
        $token_data = $wpdb->get_row( $wpdb->prepare(
                "SELECT * FROM {$wpdb->prefix}kc_ml_magic_links
    		WHERE token = %s AND expires > %s",
                $token,
                $gmt_now
        ), ARRAY_A );


        if ( ! $token_data ) {
            do_action( 'kc_ml_invalid_token' );
            wp_die( __( 'Invalid or expired magic link.', 'magic-link' ) );
        }

        $wpdb->update(
                $wpdb->prefix . 'kc_ml_magic_links',
                array( 'used' => 1 ),
                array( 'id' => $token_data['id'] ),
                array( '%d' ),
                array( '%d' )
        );

        // Log in user
        $user = get_user_by( 'id', $token_data['user_id'] );
        if ( $user ) {
            do_action( 'magic_link_before_login', $user, $token_data );

            wp_set_current_user( $user->ID );
            wp_set_auth_cookie( $user->ID, true );
            do_action( 'wp_login', $user->user_login, $user );

            // Log successful login
            $this->log_login_attempt( $user->ID, true, 'magic_link' );

            // Honour the saved redirect setting; the filter lets PRO apply
            // role-based and ?redirect_to= overrides on top.
            $default_redirect = Helper::get_user_default_redirect( $user );
            $redirect_to      = apply_filters( 'kc_ml_redirect_to_url', $default_redirect, $user );

            // Redirect
            wp_safe_redirect( $redirect_to );
            exit;
        }

        wp_die( __( 'Could not log in user.', 'magic-link' ) );
    }

    private function log_login_attempt( $user_id, $success, $method = 'magic_link' ) {
        // You can extend this to log to a separate table or use WordPress's built-in logging
        error_log( sprintf(
                'Magic Link Login: User ID %d, Success: %s, Method: %s, IP: %s',
                $user_id,
                $success ? 'Yes' : 'No',
                $method,
                $this->get_user_ip()
        ) );
    }

    private function get_user_ip() {
        $ip_keys = array( 'HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'HTTP_CLIENT_IP', 'REMOTE_ADDR' );

        foreach ( $ip_keys as $key ) {
            if ( isset( $_SERVER[ $key ] ) && ! empty( $_SERVER[ $key ] ) ) {
                $ips = explode( ',', $_SERVER[ $key ] );

                return trim( $ips[0] );
            }
        }

        return isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
    }

    function handle_login_request() {
        global $pagenow;

        if ( empty( $_GET['magic-link'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
            return;
        }

        if ( empty( $_GET['user_id'] ) || empty( $_GET['magic-token'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
            return;
        }

        // Scanner / prefetch protection. Do not consume the token unless the
        // user has explicitly confirmed intent on the form below.
        if ( IntentConfirmation::is_enabled() && ! IntentConfirmation::is_confirmation_submitted() ) {
            if ( ScannerDetector::is_current_request_scanner() ) {
                // Reply 200 with a harmless body; never touch the token store.
                echo '<!doctype html><meta name="robots" content="noindex"><title>OK</title>';
                exit;
            }

            $query = array(
                'magic-link'  => 1,
                'user_id'     => isset( $_GET['user_id'] ) ? (int) $_GET['user_id'] : 0,
                'magic-token' => isset( $_GET['magic-token'] ) ? sanitize_text_field( wp_unslash( $_GET['magic-token'] ) ) : '',
            );
            do_action( 'kc_ml_link_opened', $query['user_id'], array( 'flow' => 'admin' ) );
            echo IntentConfirmation::render( $query ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
            exit;
        }

        do_action( 'kc_ml_handle_login_request' );

        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $user_id = intval( wp_unslash( $_GET['user_id'] ) );

        if ( is_user_logged_in() ) {
            $error = __( 'Invalid magic link token', 'magic-link' );
            wp_die( $error );
        }

        $user = get_user_by( 'id', $user_id );

        if ( ! $user ) {
            do_action( 'kc_ml_invalid_user' );
            $error = __( 'Invalid User', 'magic-link' );
            wp_die( $error );
        }

        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $user_token = sanitize_text_field( wp_unslash( $_GET['magic-token'] ) );

        $tokens = Helper::get_user_tokens( $user_id );

        $is_valid = false;
        foreach ( $tokens as $i => $token_data ) {
            if ( empty( $token_data ) || ! is_array( $token_data ) || ! isset( $token_data['token'] ) ) {
                unset( $tokens[ $i ] );
                continue;
            }

            if ( Helper::validate_user_token( $user_token, $token_data['h_token'] ) ) {
                $is_valid = true;
                break;
            }
        }

        if ( ! $is_valid ) {
            do_action( 'kc_ml_invalid_token' );
            $error = __( 'Invalid Token', 'magic-link' );
            wp_die( wp_kses_post( $error ) );
        }

        wp_set_auth_cookie( $user->ID, true, is_ssl() );

        do_action( 'wp_login', $user->user_login, $user );

        $default_redirect = Helper::get_user_default_redirect( $user );
        $redirect_to      = apply_filters( 'kc_ml_redirect_to_url', $default_redirect, $user );
        wp_safe_redirect( $redirect_to );
        exit;
    }

    public static function add_magic_link_button() {
        if ( ! Helper::is_magic_link_enabled() || ! Helper::is_show_on_login_form() ) {
            return;
        }

        $login_url = Helper::get_wp_login_url();

        if ( isset( $_GET['redirect_to'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
            $login_url = esc_url_raw( add_query_arg( 'redirect_to', urlencode( $_GET['redirect_to'] ), $login_url ) ); // phpcs:ignore
        }

        ?>
        <script type="text/javascript">
			(function () {
				let loginForm = document.getElementById('loginform');

				if (loginForm) {
					loginForm.insertAdjacentHTML(
						'beforeend',
						'<div class="magic-link-normal-login">' +
						'<button type="submit" name="wp-submit" id="wp-login-submit" class="button button-primary button-hero magic-link-submit" value="<?php esc_attr_e( 'Log In', 'magic-link' ); // phpcs:ignore ?>"><?php esc_attr_e( 'Log In', 'magic-link' ); ?></button>' +
						'</div>' +
						'<span class="magic-link-or-separator"></span>' +
						'<div id="continue-with-magic-link" class="continue-with-magic-link">' +
						'<button type="button" value="<?php echo esc_url( $login_url ); ?>" class="button button-primary button-hero" id="magic-link-button">' +
						'<?php esc_html_e( 'Send me the Magic Link', 'magic-link' ); ?>' +
						'</button>' +
						'</div>'
					);

					document.getElementById('magic-link-button').onclick = function () {
						let loginInput = document.getElementById('user_login');
						if (loginInput != null && loginInput.value.length > 0) {
							let frm = document.getElementById('loginform') || null;
							if (frm) {
								frm.action = "<?php echo esc_url_raw( $login_url ); ?>";
								frm.submit();
							}
						} else {
							location.href = "<?php echo esc_url_raw( $login_url ); ?>";
						}
					}
				}
			})();
        </script>
        <?php

    }

    public function handle_send_magic_link() {
        $login_request = self::process_login_request();

        \login_header( esc_html__( 'Log in', 'magic-link' ), $login_request['info'], $login_request['errors'] );

        if ( $login_request['code_login'] ) {
        } elseif ( $login_request['show_form'] ) {
            self::login_form();
        }

        login_footer();
        exit;
    }

    public static function process_login_request( $args = [] ) {
        $defaults = [
                'info_message' => '',
        ];
        $args     = wp_parse_args( $args, $defaults );

        $response = [
                'show_form'              => true,
                'errors'                 => new \WP_Error(),
                'info'                   => '',
                'is_processed'           => false,
                'show_registration_form' => false,
                'code_login'             => false,
                'phone_login'            => false,
        ];

        // Default info message
        $response['info'] = defined( 'MAGIC_LINK_USERNAME_ONLY' ) && MAGIC_LINK_USERNAME_ONLY
                ? '<p class="message">' . __( 'Please enter your username. You will receive an email message to log in.', 'magic-link' ) . '</p>'
                : '<p class="message">' . __( 'Please enter your username or email address. You will receive an email message to log in.', 'magic-link' ) . '</p>';

        // Allow customization of info message
        $response['info'] = apply_filters( 'magic_link_info_message', $response['info'], $args );

        // Override info message if provided
        if ( ! empty( $args['info_message'] ) ) {
            $response['info'] = '<p class="message">' . esc_html( $args['info_message'] ) . '</p>';
        }

        // Handle form submission
        if ( self::is_login_form_submission() ) {
            $response = self::process_login_form_submission( $args );
        }

        // Handle magic link login request
        if ( ! empty( $_GET['magic-registration'] ) ) { // phpcs:ignore
            $response['show_registration_form'] = true;
        }

        /**
         * Filter the result of the login process
         *
         * @hook  magic_link_process_login_request_result
         *
         * @param  array  $response  Result of the login process
         * @param  array  $args      Arguments passed to the function
         *
         * @since 2.4
         */
        return apply_filters( 'magic_link_process_login_request_result', $response, $args );
    }

    public static function process_login_form_submission( $args ) {
        $show_form              = true;
        $show_registration_form = false;
        $code_login             = false;
        $user_name              = isset( $_POST['log'] ) ? sanitize_user( wp_unslash( $_POST['log'] ) ) : '';
        $user                   = Helper::get_user_by_log_input( $user_name );
        $is_processed           = true;

        // If no user found and input is an email, check trusted domains.
        if ( ! $user && is_email( $user_name ) ) {
            $maybe_user = Helper::maybe_create_trusted_domain_user( $user_name );
            if ( ! is_wp_error( $maybe_user ) ) {
                $user = $maybe_user;
            }
        }
        $phone_login            = false;
        $info                   = '';

        // Apply pre-process filter
        $result = apply_filters( 'magic_link_pre_process_login_request', null );

        if ( is_wp_error( $result ) ) {
            return [
                    'is_processed'           => $is_processed,
                    'errors'                 => $result,
                    'info'                   => $info,
                    'show_form'              => $show_form,
                    'show_registration_form' => $show_registration_form,
            ];
        }

        // Apply pre-send link filter
        $send_link = apply_filters( 'magic_link_pre_send_login_link', null, $user );
        $errors    = null;

        if ( ! is_a( $user, '\WP_User' ) ) {
            $error_code = 'missing_user';

            if ( defined( 'MAGIC_LINK_USERNAME_ONLY' ) && MAGIC_LINK_USERNAME_ONLY ) {
                $error_message = esc_html__( 'There is no account with that username.', 'magic-link' );
            } else {
                $error_message = esc_html__( 'There is no account with that username or email address.', 'magic-link' );
            }

            if ( $code_login && $phone_login ) {
                $error_message = esc_html__( 'Unable to process your request. Please try again.', 'magic-link' );
            }

            if ( ! empty( $args['error_message'] ) ) {
                $error_message = $args['error_message'];
            }

            $error_message = apply_filters( 'magic_link_missing_user_error_message', $error_message, $error_code, $args );

            $errors      = new \WP_Error( $error_code, $error_message );
            $show_form   = true;
            $code_login  = false;
            $phone_login = false;
        } elseif ( null !== $send_link ) {
            $errors      = $send_link;
            $show_form   = false;
            $code_login  = false;
            $phone_login = false;
        } else {
            global $magic_link_code_login_result, $magic_link_link;
            if ( ! empty( $magic_link_code_login_result ) ) {
                // after post request, if code login failed, show error message
                $errors = $magic_link_code_login_result;
            } else {
                $email = $user->user_email;
                $data  = [
                        'email' => $email,
                ];

                $magic_link = Helper::generate_magic_link( $data );

                if ( is_wp_error( $magic_link ) ) {
                    $errors = $magic_link;
                } else {
                    $errors = Helper::send_magic_link_email( $user, $magic_link );
                }
            }
        }

        if ( ! is_wp_error( $errors ) ) {
            $show_form = false;
            $info      = '<p class="message magic_link_block_login_success">' . __( 'Please check your inbox for the login link. If you did not receive a login email, check your spam folder too.', 'magic-link' ) . '</p>';

            if ( $phone_login ) {
                $info = '<p class="message magic_link_block_login_success">' . __( 'Please check your phone for the login link.', 'magic-link' ) . '</p>';
            }

            if ( ! empty( $args['success_message'] ) ) {
                $info = '<p class="message magic_link_block_login_success">' . $args['success_message'] . '</p>';
            }

            if ( $code_login ) {
                $info = '<p class="message magic_link_block_login_success">' . __( 'Please enter the code sent to your email.', 'magic-link' ) . '</p>';

                if ( ! empty( $args['code_success_message'] ) ) {
                    $info = '<p class="message magic_link_block_login_success">' . $args['code_success_message'] . '</p>';
                }
            }
        }

        return [
                'is_processed'           => $is_processed,
                'errors'                 => $errors,
                'info'                   => $info,
                'show_form'              => $show_form,
                'show_registration_form' => $show_registration_form,
                'code_login'             => $code_login,
                'phone_login'            => $phone_login,
        ];
    }

    /**
     * Determines if the current request is a login form submission.
     *
     * @return bool
     * @since 2.4
     */
    private static function is_login_form_submission() {
        return isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'] && ! empty( $_POST['log'] );
    }

    /**
     * Maybe redirect to magic link login form if force magic link login is enabled.
     *
     * @return void
     *
     * @since 1.1.8
     */
    public function maybe_redirect() {
        global $pagenow;

        if ( 'wp-login.php' !== $pagenow ) {
            return;
        }

        if ( ! empty( ( $_POST ) ) ) {
            return;
        }

        if ( null !== apply_filters( 'magic_link_before_login_form_redirect', null ) ) {
            return;
        }

        $settings = KC_ML()->get_settings();

        $is_force_magic_link_login = Helper::get_data( $settings, 'general|settings|force_magic_link_login', false );

        if ( $is_force_magic_link_login ) {
            wp_safe_redirect( esc_url_raw( add_query_arg( 'action', 'magic_link' ) ) );
            exit;
        }
    }

    public static function login_form() {
        $user_login = '';

        $login_url = Helper::get_wp_login_url();
        if ( isset( $_POST['log'] ) && is_string( $_POST['log'] ) ) {
            $user_login = wp_unslash( $_POST['log'] ); // phpcs:ignore
        }

        ?>
        <form
                name="magicloginform"
                id="magicloginform"
                action="<?php
                echo esc_url( $login_url ); ?>"
                method="post"
                autocomplete="off"
                data-ajax-url="<?php
                echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>"
                data-ajax-spinner="<?php
                echo esc_url( get_admin_url() . 'images/spinner.gif' ); ?>"
                data-ajax-sending-msg="<?php
                esc_attr_e( 'Sending...', 'magic-link' ); ?>"
                data-spam-protection-msg="<?php
                esc_attr_e( 'Please verify that you are not a robot.', 'magic-link' ); ?>"
        >
            <p>
                <?php
                if ( defined( 'MAGIC_LINK_USERNAME_ONLY' ) && MAGIC_LINK_USERNAME_ONLY ) : ?>
                    <label for="user_login"><?php
                        esc_html_e( 'Username', 'magic-link' ); ?></label>
                <?php
                else : ?>
                    <label for="user_login"><?php
                        esc_html_e( 'Username or Email Address', 'magic-link' ); ?></label>
                <?php
                endif; ?>
                <input type="text" name="log" id="user_login" class="input" value="<?php
                echo esc_attr( $user_login ); ?>" size="20" autocapitalize="off" autocomplete="username" required/>
            </p>
            <?php

            /**
             * Fires following the 'email' field in the login form.
             *
             * @since 1.0
             */
            do_action( 'magic_link_form' );

            ?>
            <p class="submit">
                <input type="submit" name="wp-submit" id="wp-submit" style="float: none;width: 100%;"
                       class="magic-link-submit button button-primary button-hero" value="<?php
                esc_attr_e( 'Send me the Magic Link', 'magic-link' ); ?>"/>
                <?php
                if ( isset( $_GET['redirect_to'] ) ) : // phpcs:ignore WordPress.Security.NonceVerification.Recommended ?>
                    <input type="hidden" name="redirect_to" value="<?php
                    echo esc_url( $_GET['redirect_to'] ); // phpcs:ignore ?>">
                <?php
                endif; ?>
                <input type="hidden" name="testcookie" value="1"/>
            </p>
        </form>
        <?php
    }

    public static function login_css() {
        ?>
        <style>

            form[name="validate_2fa_form"] #magic-login-button,
            form[name="validate_2fa_form"] .magic-login-or-separator {
                display: none;
            }

            form[name="validate_2fa_form"] .submit {
                display: none;
            }

            .two-factor-email-resend input[type="submit"] {
                width: 100%;
                margin: auto;
                display: block;
                text-align: center;
                padding: 0 36px;
                min-height: 46px;
            }


            #loginform #wp-submit {
                display: none;
            }

            .magic-link-normal-login {
                width: 100%;
                margin: auto;
                padding-top: 10px;
                display: block;
                text-align: center;
                clear: both;
            }

            .magic-link-normal-login .button,
            #magic-link-button {
                width: 100%;
                float: none !important;
            }

            #magic-link-button {
                padding: unset !important;
            }

            .continue-with-magic-link {
                width: 100%;
                margin: auto;
                display: block;
                text-align: center;
            }

            .continue-with-magic-link .button {
                float: none;
            }

            .magic-link-or-separator {
                display: block;
                text-align: center;
                position: relative;
                margin: 10px auto;
                width: 100%;
            }

            .magic-link-or-separator:before {
                content: "<?php esc_html_e( 'or', 'magic-link' ); ?>";
                background-color: #fff;
                font-size: 13px;
                color: #9b9b9b;
                display: inline-block;
                width: 62px;
                position: relative;
                z-index: 1;
            }

            .magic-link-or-separator:after {
                content: "";
                width: 100%;
                position: absolute;
                left: 0;
                top: 50%;
                height: 1px;
                margin-top: -0.5px;
                background-color: #d8d8d8;
            }

            .magic-link-captcha-wrapper {
                text-align: center;
                margin: 10px 0;
                width: 100%;
            }

            /* Target reCAPTCHA v2 checkbox (not invisible) */
            .magic-link-captcha-wrapper .g-recaptcha:not([data-size="invisible"]) {
                transform: scale(0.90);
                transform-origin: 0 0;
                display: inline-block;
            }

            /* No scaling for invisible reCAPTCHA */
            .magic-link-captcha-wrapper .g-recaptcha[data-size="invisible"] {
                transform: none;
                display: inline;
            }

            .magic-link-captcha-wrapper iframe,
            .magic-link-captcha-wrapper .grecaptcha-badge {
                width: 100% !important;
            }

            .magic-link-captcha-error {
                color: #d63638;
            }

            #cf-turnstile-container {
                width: 100%;
                transform: scale(0.9);
                transform-origin: top left;
            }

        </style>
        <?php
    }


}
