<?php
/**
 * Optional email verification before local subscription shows as active (Stripe billing still runs).
 *
 * @package Invizo\SubscriptionsLite\Frontend
 */

namespace Invizo\SubscriptionsLite\Frontend;

use Exception;

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

class Subscription_Email_Verification {

    const META_PENDING = '_invizo_email_verification_pending';
    const META_TOKEN   = '_invizo_email_verify_token';
    const META_EXPIRES = '_invizo_email_verify_expires';

    public static function init() {
        add_action( 'template_redirect', array( __CLASS__, 'handle_verify_link' ), 5 );
    }

    /**
     * @return bool
     */
    public static function is_enabled() {
        return get_option( 'invizo_sub_require_email_verification', '0' ) === '1';
    }

    /**
     * @param int $sub_post_id invizo_sub post ID.
     * @return bool
     */
    public static function is_subscription_pending_verification( $sub_post_id ) {
        return get_post_meta( $sub_post_id, self::META_PENDING, true ) === '1';
    }

    /**
     * Set subscription active locally or pending until the customer clicks the email link.
     *
     * @param int    $sub_post_id invizo_sub post ID.
     * @param string $email       Customer email.
     * @param string $fname       Display / first name (optional).
     * @return bool True when verification email was sent (pending flow).
     */
    public static function finalize_new_subscription_status( $sub_post_id, $email, $fname = '' ) {
        $sub_post_id = (int) $sub_post_id;
        if ( $sub_post_id <= 0 ) {
            return false;
        }

        if ( ! self::is_enabled() || ! is_email( $email ) ) {
            update_post_meta( $sub_post_id, '_status', 'active' );
            delete_post_meta( $sub_post_id, self::META_PENDING );
            delete_post_meta( $sub_post_id, self::META_TOKEN );
            delete_post_meta( $sub_post_id, self::META_EXPIRES );
            return false;
        }

        update_post_meta( $sub_post_id, '_status', 'pending' );
        update_post_meta( $sub_post_id, self::META_PENDING, '1' );

        if ( function_exists( 'random_bytes' ) ) {
            try {
                $token = bin2hex( random_bytes( 20 ) );
            } catch ( Exception $e ) {
                $token = wp_generate_password( 40, false, false );
            }
        } else {
            $token = wp_generate_password( 40, false, false );
        }

        update_post_meta( $sub_post_id, self::META_TOKEN, $token );
        update_post_meta( $sub_post_id, self::META_EXPIRES, (string) ( time() + 3 * DAY_IN_SECONDS ) );

        $verify_url = add_query_arg(
            array(
                'invizo_verify_subscription' => '1',
                'sid'                        => $sub_post_id,
                'token'                      => $token,
            ),
            home_url( '/' )
        );

        $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
        $subject  = sprintf(
            /* translators: %s: site name */
            __( '[%s] Confirm your subscription', 'invizo-subscriptions-lite' ),
            $blogname
        );

        $greeting = $fname !== ''
            ? sprintf(
                /* translators: %s: customer first name */
                __( 'Hi %s,', 'invizo-subscriptions-lite' ),
                $fname
            )
            : __( 'Hi,', 'invizo-subscriptions-lite' );

        $body  = $greeting . "\r\n\r\n";
        $body .= __( 'Thank you for subscribing. Please confirm your email address to activate your subscription in our member area.', 'invizo-subscriptions-lite' ) . "\r\n\r\n";
        $body .= $verify_url . "\r\n\r\n";
        $body .= __( 'This link expires in three days. If you did not subscribe, you can ignore this email.', 'invizo-subscriptions-lite' ) . "\r\n";

        $body = apply_filters( 'invizo_subscription_verification_email_body', $body, $verify_url, $sub_post_id, $email );

        wp_mail(
            $email,
            $subject,
            $body,
            array( 'Content-Type: text/plain; charset=UTF-8' )
        );

        return true;
    }

    /**
     * Public verify endpoint (GET).
     */
    public static function handle_verify_link() {
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- email link uses secret token + hash_equals(), not nonces; keys checked with isset().
        if ( ! isset( $_GET['invizo_verify_subscription'], $_GET['sid'], $_GET['token'] ) ) {
            return;
        }

        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- verified flag for email confirmation link.
        if ( (string) sanitize_text_field( wp_unslash( $_GET['invizo_verify_subscription'] ) ) !== '1' ) {
            return;
        }

        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- subscription post ID from signed email link; cast below.
        $sid = absint( wp_unslash( $_GET['sid'] ) );
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- token compared with hash_equals() to stored secret.
        $token = sanitize_text_field( wp_unslash( $_GET['token'] ) );

        $post = get_post( $sid );
        if ( ! $post || $post->post_type !== 'invizo_sub' ) {
            self::redirect_after_verify( 'invalid' );
        }

        $stored  = (string) get_post_meta( $sid, self::META_TOKEN, true );
        $expires = (int) get_post_meta( $sid, self::META_EXPIRES, true );

        if ( $stored === '' || ! hash_equals( $stored, $token ) ) {
            self::redirect_after_verify( 'invalid' );
        }

        if ( $expires > 0 && time() > $expires ) {
            self::redirect_after_verify( 'expired' );
        }

        delete_post_meta( $sid, self::META_TOKEN );
        delete_post_meta( $sid, self::META_EXPIRES );
        update_post_meta( $sid, self::META_PENDING, '0' );
        update_post_meta( $sid, '_status', 'active' );

        self::redirect_after_verify( 'ok' );
    }

    /**
     * @param string $result ok|invalid|expired
     */
    private static function redirect_after_verify( $result ) {
        $base = class_exists( Auth::class, false )
            ? Auth::get_account_page_url()
            : home_url( '/' );

        if ( $result === 'ok' ) {
            wp_safe_redirect( add_query_arg( 'invizo_email_verified', '1', $base ) );
        } elseif ( $result === 'expired' ) {
            wp_safe_redirect( add_query_arg( 'invizo_verify', 'expired', $base ) );
        } else {
            wp_safe_redirect( add_query_arg( 'invizo_verify', 'invalid', $base ) );
        }
        exit;
    }
}
