<?php

namespace WPVOID\StockBeacon\Integrations;

use WPVOID\StockBeacon\Core\SignupValidator;
use WPVOID\StockBeacon\Core\Throttle;
use WPVOID\StockBeacon\Core\WaitlistManager;
use WPVOID\StockBeacon\Frontend\FormRenderer;
use WPVOID\StockBeacon\Notifications\EmailManager;
use WPVOID\StockBeacon\Settings;
if ( !defined( 'ABSPATH' ) ) {
    exit;
}
class WooCommerceHooks {
    private WaitlistManager $manager;

    private Throttle $throttle;

    private FormRenderer $form;

    private EmailManager $email;

    /** @var object|null Opt-in token service; only provided by the Pro package. */
    private $optin_service;

    /** @var array<int, int|null> Stock quantities captured immediately before WooCommerce updates them. */
    private array $stock_quantities_before_update = array();

    public function __construct(
        WaitlistManager $manager,
        Throttle $throttle,
        FormRenderer $form,
        EmailManager $email,
        $optin_service = null
    ) {
        $this->manager = $manager;
        $this->throttle = $throttle;
        $this->form = $form;
        $this->email = $email;
        $this->optin_service = $optin_service;
    }

    public function register() : void {
        add_action( 'woocommerce_single_product_summary', array($this, 'maybe_render_waitlist_form'), $this->get_form_position_priority() );
        add_action( 'woocommerce_after_variations_form', array($this, 'render_variation_waitlist_container') );
        add_filter(
            'woocommerce_available_variation',
            array($this, 'add_waitlist_data_to_variation'),
            10,
            3
        );
        add_action( 'wp_ajax_stockbeacon_subscribe', array($this, 'handle_subscribe') );
        add_action( 'wp_ajax_nopriv_stockbeacon_subscribe', array($this, 'handle_subscribe') );
        add_action(
            'woocommerce_product_set_stock_status',
            array($this, 'on_stock_status_change'),
            10,
            3
        );
        add_action(
            'woocommerce_variation_set_stock_status',
            array($this, 'on_variation_stock_status_change'),
            10,
            3
        );
        add_action(
            'woocommerce_product_set_stock_status',
            array($this, 'on_product_out_of_stock_reset_notified'),
            15,
            3
        );
        add_action(
            'woocommerce_variation_set_stock_status',
            array($this, 'on_variation_out_of_stock_reset_notified'),
            15,
            3
        );
        add_action( 'wp_enqueue_scripts', array($this, 'enqueue_assets') );
        add_action( 'init', array($this, 'handle_unsubscribe') );
    }

    public function maybe_render_waitlist_form() : void {
        if ( !$this->plugin_enabled() ) {
            return;
        }
        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- WooCommerce sets $product on single product templates.
        global $product;
        if ( !$product instanceof \WC_Product || $product->is_type( 'variable' ) ) {
            return;
        }
        if ( SignupValidator::is_waitlist_hidden( $product->get_id() ) ) {
            return;
        }
        if ( !SignupValidator::is_waitlist_available( $product, $product->get_id() ) ) {
            return;
        }
        $this->form->render( $product->get_id(), 0 );
    }

    public function render_variation_waitlist_container() : void {
        if ( !$this->plugin_enabled() ) {
            return;
        }
        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- WooCommerce sets $product on single product templates.
        global $product;
        if ( !$product instanceof \WC_Product || !$product->is_type( 'variable' ) ) {
            return;
        }
        if ( SignupValidator::is_waitlist_hidden( $product->get_id() ) ) {
            return;
        }
        echo '<div id="stockbeacon-variation-form" style="display:none">';
        $this->form->render( $product->get_id(), 0 );
        echo '</div>';
    }

    public function add_waitlist_data_to_variation( array $data, \WC_Product $product, \WC_Product_Variation $variation ) : array {
        if ( !$this->plugin_enabled() ) {
            $data['stockbeacon_active'] = false;
            $data['stockbeacon_count'] = 0;
            return $data;
        }
        $data['stockbeacon_active'] = !SignupValidator::is_waitlist_hidden( $product->get_id() ) && SignupValidator::is_waitlist_available( $variation, $product->get_id() );
        $data['stockbeacon_count'] = $this->manager->count_for_display( $product->get_id(), $variation->get_id() );
        return $data;
    }

    public function handle_subscribe() : void {
        check_ajax_referer( 'stockbeacon_subscribe', '_wpnonce' );
        if ( !$this->plugin_enabled() ) {
            wp_send_json_error( array(
                'message' => __( 'Waitlist signups are currently disabled.', 'stockbeacon-product-waitlists-for-woocommerce' ),
            ) );
        }
        $email = SignupValidator::normalize_email( sanitize_email( (string) wp_unslash( $_POST['email'] ?? '' ) ) );
        $product_id = absint( wp_unslash( $_POST['product_id'] ?? 0 ) );
        $consent = absint( wp_unslash( $_POST['consent'] ?? 0 ) );
        if ( !is_email( $email ) || !$product_id ) {
            wp_send_json_error( array(
                'message' => __( 'Please enter a valid email and product.', 'stockbeacon-product-waitlists-for-woocommerce' ),
            ) );
        }
        if ( SignupValidator::consent_required() && !$consent ) {
            wp_send_json_error( array(
                'message' => __( 'Please check the consent box.', 'stockbeacon-product-waitlists-for-woocommerce' ),
            ) );
        }
        $variation_id = ( isset( $_POST['variation_id'] ) ? absint( wp_unslash( $_POST['variation_id'] ) ) : 0 );
        $eligible = SignupValidator::validate_subscribe_eligibility( $product_id, $variation_id );
        if ( is_wp_error( $eligible ) ) {
            wp_send_json_error( array(
                'message' => $eligible->get_error_message(),
            ) );
        }
        $product = wc_get_product( $product_id );
        if ( !$product instanceof \WC_Product ) {
            wp_send_json_error( array(
                'message' => __( 'This product is not currently eligible for waitlist signup.', 'stockbeacon-product-waitlists-for-woocommerce' ),
            ) );
        }
        $ip = sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0' ) );
        if ( !$this->throttle->is_allowed( $ip, max( 1, (int) (Settings::get()['throttle_per_minute'] ?? 5) ) ) ) {
            wp_send_json_error( array(
                'message' => __( 'Too many requests. Please wait and try again.', 'stockbeacon-product-waitlists-for-woocommerce' ),
            ) );
        }
        if ( $this->manager->exists_active( $email, $product_id, $variation_id ) ) {
            $pending = $this->manager->get_pending_by_email_product( $email, $product_id, $variation_id );
            if ( $pending && !$this->is_double_optin_active() ) {
                $this->manager->activate_pending_without_optin( (int) $pending->id, $product_id );
            }
            $this->send_idempotent_subscription_response();
            return;
        }
        $status = 'active';
        $token_raw = null;
        $token_hash = null;
        $settings = Settings::get();
        $id = $this->manager->add_or_resubscribe( array(
            'email'        => $email,
            'product_id'   => $product_id,
            'variation_id' => $variation_id,
            'user_id'      => get_current_user_id(),
            'status'       => $status,
            'opt_in_token' => $token_hash,
            'ip_address'   => $ip,
            'consent'      => $consent,
        ) );
        if ( !$id ) {
            if ( $this->manager->exists_active( $email, $product_id, $variation_id ) ) {
                $pending = $this->manager->get_pending_by_email_product( $email, $product_id, $variation_id );
                if ( $pending && !$this->is_double_optin_active() ) {
                    $this->manager->activate_pending_without_optin( (int) $pending->id, $product_id );
                }
                $this->send_idempotent_subscription_response();
                return;
            }
            wp_send_json_error( array(
                'message' => __( 'Unable to save subscription.', 'stockbeacon-product-waitlists-for-woocommerce' ),
            ) );
        }
        $this->manager->bust_count_cache( $product_id );
        $this->send_idempotent_subscription_response();
    }

    private function send_idempotent_subscription_response() : void {
        wp_send_json_success( array(
            'message' => $this->get_subscription_received_message(),
        ) );
    }

    /**
     * Success message for the storefront form. It depends only on whether the
     * double opt-in flow is active — never on the individual request — so the
     * response stays identical for new and repeat submissions and does not
     * reveal whether an email address is already subscribed.
     */
    private function get_subscription_received_message() : string {
        if ( $this->is_double_optin_active() ) {
            return __( 'Your waitlist request has been received. If confirmation is required, please check your email.', 'stockbeacon-product-waitlists-for-woocommerce' );
        }
        $message = trim( (string) (Settings::get()['form_success_message'] ?? '') );
        if ( '' !== $message ) {
            return $message;
        }
        return __( "You're on the list! We'll email you when this item is restocked.", 'stockbeacon-product-waitlists-for-woocommerce' );
    }

    /**
     * Whether the double opt-in confirmation flow can actually run for signups.
     */
    private function is_double_optin_active() : bool {
        $active = false;
        return $active;
    }

    public function on_stock_status_change( int $product_id, string $status, $product = null ) : void {
        if ( 'instock' !== $status ) {
            return;
        }
        $wc = ( $product instanceof \WC_Product ? $product : wc_get_product( $product_id ) );
        if ( !$wc instanceof \WC_Product || !$wc->is_in_stock() ) {
            return;
        }
        if ( get_transient( 'stockbeacon_restock_sent_' . $product_id ) ) {
            return;
        }
        set_transient( 'stockbeacon_restock_sent_' . $product_id, 1, 5 * MINUTE_IN_SECONDS );
        if ( $wc->is_type( 'variable' ) ) {
            $variation_ids = $this->manager->get_distinct_variation_ids_with_active_waiters( $product_id );
            if ( empty( $variation_ids ) ) {
                delete_transient( 'stockbeacon_restock_sent_' . $product_id );
                return;
            }
            foreach ( $variation_ids as $vid ) {
                $this->email->schedule_restock_batch( $product_id, (int) $vid );
            }
            return;
        }
        $this->email->schedule_restock_batch( $product_id, 0 );
    }

    public function on_variation_stock_status_change( int $variation_id, string $status, $product = null ) : void {
        if ( 'instock' !== $status ) {
            return;
        }
        $variation = ( $product instanceof \WC_Product_Variation ? $product : wc_get_product( $variation_id ) );
        if ( !$variation instanceof \WC_Product_Variation ) {
            return;
        }
        $parent_id = (int) $variation->get_parent_id();
        if ( $parent_id <= 0 ) {
            return;
        }
        delete_transient( 'stockbeacon_restock_sent_' . $parent_id . '_' . (int) $variation_id );
        delete_transient( 'stockbeacon_restock_lock_' . $parent_id . '_' . (int) $variation_id );
        delete_transient( 'stockbeacon_restock_sent_' . $parent_id );
        $this->email->schedule_restock_batch( $parent_id, (int) $variation_id );
    }

    /**
     * When a simple or parent product becomes unavailable, prior restock emails should not leave shoppers stuck as "notified".
     */
    public function on_product_out_of_stock_reset_notified( int $product_id, string $status, $product = null ) : void {
        if ( !in_array( $status, array('outofstock', 'onbackorder'), true ) ) {
            return;
        }
        if ( 'remove' === (string) (Settings::get()['restock_subscriber_retention'] ?? 'keep') ) {
            return;
        }
        $wc_product = ( $product instanceof \WC_Product ? $product : wc_get_product( $product_id ) );
        if ( !$wc_product instanceof \WC_Product ) {
            return;
        }
        if ( $wc_product->is_type( 'variable' ) ) {
            $this->manager->reset_notified_subscribers_to_active( $product_id, null );
            $this->manager->clear_restock_send_locks( $product_id );
            return;
        }
        $this->manager->reset_notified_subscribers_to_active( $product_id, 0 );
        $this->manager->clear_restock_send_locks( $product_id );
    }

    /**
     * When a variation becomes unavailable, reset notified subscribers for that variation and any applicable legacy parent-level rows.
     */
    public function on_variation_out_of_stock_reset_notified( int $variation_id, string $status, $product = null ) : void {
        if ( !in_array( $status, array('outofstock', 'onbackorder'), true ) ) {
            return;
        }
        if ( 'remove' === (string) (Settings::get()['restock_subscriber_retention'] ?? 'keep') ) {
            return;
        }
        $variation = ( $product instanceof \WC_Product_Variation ? $product : wc_get_product( $variation_id ) );
        if ( !$variation instanceof \WC_Product_Variation ) {
            return;
        }
        $parent_id = (int) $variation->get_parent_id();
        if ( $parent_id <= 0 ) {
            return;
        }
        $this->manager->reset_notified_subscribers_to_active( $parent_id, (int) $variation_id );
        delete_transient( 'stockbeacon_restock_sent_' . $parent_id . '_' . (int) $variation_id );
        delete_transient( 'stockbeacon_restock_lock_' . $parent_id . '_' . (int) $variation_id );
        $can_reset_parent_level = true;
        if ( $can_reset_parent_level ) {
            $this->manager->reset_notified_subscribers_to_active( $parent_id, 0 );
        }
        delete_transient( 'stockbeacon_restock_sent_' . $parent_id );
    }

    public function enqueue_assets() : void {
        if ( !is_product() ) {
            return;
        }
        $stockbeacon_style_deps = array();
        if ( wp_style_is( 'woocommerce-general', 'registered' ) || wp_style_is( 'woocommerce-general', 'enqueued' ) ) {
            $stockbeacon_style_deps[] = 'woocommerce-general';
        }
        wp_enqueue_style(
            'stockbeacon-frontend',
            STOCKBEACON_URL . 'assets/css/frontend.css',
            $stockbeacon_style_deps,
            STOCKBEACON_VERSION
        );
        wp_enqueue_script(
            'stockbeacon-frontend',
            STOCKBEACON_URL . 'assets/js/frontend.js',
            array('jquery'),
            STOCKBEACON_VERSION,
            true
        );
        $settings = Settings::get();
        $accent = (string) ($settings['form_accent_color'] ?? '#2563eb');
        $accent = ( sanitize_hex_color( $accent ) ? $accent : '#2563eb' );
        $form_theme = ( 'dark' === (string) ($settings['form_theme_mode'] ?? 'light') ? 'dark' : 'light' );
        $form_bg_raw = (string) ($settings['form_background_color'] ?? '#ffffff');
        $form_text_raw = (string) ($settings['form_text_color'] ?? '#0f172a');
        $form_muted_raw = (string) ($settings['form_description_color'] ?? '#64748b');
        $form_input_bg_raw = (string) ($settings['form_input_background_color'] ?? '#ffffff');
        $form_count_text_raw = (string) ($settings['form_count_text_color'] ?? (( 'dark' === $form_theme ? '#14532d' : '#334155' )));
        $form_bg = ( sanitize_hex_color( ( 'dark' === $form_theme && '#ffffff' === strtolower( $form_bg_raw ) ? '#101827' : $form_bg_raw ) ) ?: '#ffffff' );
        $form_text = ( sanitize_hex_color( ( 'dark' === $form_theme && '#0f172a' === strtolower( $form_text_raw ) ? '#f8fafc' : $form_text_raw ) ) ?: '#0f172a' );
        $form_muted = ( sanitize_hex_color( ( 'dark' === $form_theme && '#64748b' === strtolower( $form_muted_raw ) ? '#cbd5e1' : $form_muted_raw ) ) ?: '#64748b' );
        $form_input_bg = ( sanitize_hex_color( ( 'dark' === $form_theme && '#ffffff' === strtolower( $form_input_bg_raw ) ? '#0f172a' : $form_input_bg_raw ) ) ?: '#ffffff' );
        $form_count_text = ( sanitize_hex_color( ( 'dark' === $form_theme && '#334155' === strtolower( $form_count_text_raw ) ? '#14532d' : $form_count_text_raw ) ) ?: (( 'dark' === $form_theme ? '#14532d' : '#334155' )) );
        $btn_color = (string) ($settings['form_button_color'] ?? $accent);
        $button = ( sanitize_hex_color( $btn_color ) ? $btn_color : $accent );
        list( $form_input_text, $form_input_placeholder ) = $this->get_readable_input_colors( $form_input_bg );
        $shadow_rule = ( empty( $settings['form_box_shadow_enabled'] ) ? '--stockbeacon-shadow:none;' : '' );
        $title_font_size = min( 32, max( 12, absint( $settings['form_title_font_size'] ?? 17 ) ) );
        $description_font_size = min( 24, max( 11, absint( $settings['form_description_font_size'] ?? 14 ) ) );
        $input_font_size = min( 22, max( 12, absint( $settings['form_input_font_size'] ?? 16 ) ) );
        $button_font_size = min( 22, max( 12, absint( $settings['form_button_font_size'] ?? 14 ) ) );
        $count_font_size = min( 18, max( 10, absint( $settings['form_count_font_size'] ?? 13 ) ) );
        $success_font_size = min( 24, max( 11, absint( $settings['form_success_font_size'] ?? 15 ) ) );
        $radius = 6;
        wp_add_inline_style( 'stockbeacon-frontend', ':root{--stockbeacon-accent:' . esc_attr( $accent ) . ';--stockbeacon-button:' . esc_attr( $button ) . ';--stockbeacon-radius:' . absint( $radius ) . 'px;--stockbeacon-bg:' . esc_attr( $form_bg ) . ';--stockbeacon-text:' . esc_attr( $form_text ) . ';--stockbeacon-text-muted:' . esc_attr( $form_muted ) . ';--stockbeacon-input-bg:' . esc_attr( $form_input_bg ) . ';--stockbeacon-input-text:' . esc_attr( $form_input_text ) . ';--stockbeacon-input-placeholder:' . esc_attr( $form_input_placeholder ) . ';--stockbeacon-count-text:' . esc_attr( $form_count_text ) . ';' . esc_attr( $shadow_rule ) . '--stockbeacon-title-font-size:' . absint( $title_font_size ) . 'px;--stockbeacon-description-font-size:' . absint( $description_font_size ) . 'px;--stockbeacon-input-font-size:' . absint( $input_font_size ) . 'px;--stockbeacon-button-font-size:' . absint( $button_font_size ) . 'px;--stockbeacon-count-font-size:' . absint( $count_font_size ) . 'px;--stockbeacon-success-font-size:' . absint( $success_font_size ) . 'px;}' );
        $redirect_url = '';
        wp_localize_script( 'stockbeacon-frontend', 'stockbeaconData', array(
            'ajaxUrl'     => admin_url( 'admin-ajax.php' ),
            'nonce'       => wp_create_nonce( 'stockbeacon_subscribe' ),
            'redirectUrl' => $redirect_url,
            'i18n'        => array(
                'submitting'       => __( 'Submitting...', 'stockbeacon-product-waitlists-for-woocommerce' ),
                'subscribing'      => __( 'Subscribing…', 'stockbeacon-product-waitlists-for-woocommerce' ),
                'invalid_email'    => __( 'Please enter a valid email address.', 'stockbeacon-product-waitlists-for-woocommerce' ),
                'consent_required' => __( 'Please check the consent box.', 'stockbeacon-product-waitlists-for-woocommerce' ),
                'error'            => __( 'Something went wrong. Please try again.', 'stockbeacon-product-waitlists-for-woocommerce' ),
                'button_text'      => $settings['form_button_text'],
            ),
        ) );
    }

    public function handle_unsubscribe() : void {
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Routing check only (no value is read); process_unsubscribe_confirmation() verifies the stockbeacon_unsubscribe_confirm nonce before any state change.
        if ( isset( $_POST['stockbeacon_unsubscribe_confirm'] ) ) {
            $this->process_unsubscribe_confirmation();
            return;
        }
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only: renders the confirmation form below; the state change happens in process_unsubscribe_confirmation() behind a nonce.
        $token = ( isset( $_GET['stockbeacon_unsubscribe'] ) ? sanitize_text_field( wp_unslash( $_GET['stockbeacon_unsubscribe'] ) ) : '' );
        if ( '' === $token ) {
            return;
        }
        if ( !preg_match( '/\\A[0-9a-f]{64}\\z/D', $token ) ) {
            wp_die( esc_html__( 'Invalid unsubscribe link.', 'stockbeacon-product-waitlists-for-woocommerce' ), '', array(
                'response' => 400,
            ) );
        }
        $this->render_unsubscribe_confirmation( $token );
    }

    /**
     * The unsubscribe email link only renders this confirmation form; unsubscribing requires the nonce-verified POST it submits.
     */
    private function render_unsubscribe_confirmation( string $token ) : void {
        $message = '<p>' . esc_html__( 'Do you want to stop receiving restock notifications for this waitlist signup?', 'stockbeacon-product-waitlists-for-woocommerce' ) . '</p>';
        $message .= '<form method="post" action="' . esc_url( home_url( '/' ) ) . '">';
        $message .= wp_nonce_field(
            'stockbeacon_unsubscribe_confirm',
            '_wpnonce',
            false,
            false
        );
        $message .= '<input type="hidden" name="stockbeacon_unsubscribe_confirm" value="1" />';
        $message .= '<input type="hidden" name="stockbeacon_unsubscribe_token" value="' . esc_attr( $token ) . '" />';
        $message .= '<p><button type="submit" class="button">' . esc_html__( 'Yes, unsubscribe me', 'stockbeacon-product-waitlists-for-woocommerce' ) . '</button></p>';
        $message .= '</form>';
        $allowed = array(
            'p'      => array(),
            'form'   => array(
                'method' => true,
                'action' => true,
            ),
            'input'  => array(
                'type'  => true,
                'name'  => true,
                'value' => true,
                'id'    => true,
            ),
            'button' => array(
                'type'  => true,
                'class' => true,
            ),
        );
        wp_die( wp_kses( $message, $allowed ), esc_html__( 'Confirm unsubscribe', 'stockbeacon-product-waitlists-for-woocommerce' ), array(
            'response' => 200,
        ) );
    }

    private function process_unsubscribe_confirmation() : void {
        $nonce = ( isset( $_POST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ) : '' );
        if ( !wp_verify_nonce( $nonce, 'stockbeacon_unsubscribe_confirm' ) ) {
            wp_die( esc_html__( 'This confirmation has expired. Please open the unsubscribe link from your email again.', 'stockbeacon-product-waitlists-for-woocommerce' ), '', array(
                'response' => 403,
            ) );
        }
        $token = ( isset( $_POST['stockbeacon_unsubscribe_token'] ) ? sanitize_text_field( wp_unslash( $_POST['stockbeacon_unsubscribe_token'] ) ) : '' );
        if ( !preg_match( '/\\A[0-9a-f]{64}\\z/D', $token ) ) {
            wp_die( esc_html__( 'Invalid unsubscribe link.', 'stockbeacon-product-waitlists-for-woocommerce' ), '', array(
                'response' => 400,
            ) );
        }
        wp_die( ( $this->manager->unsubscribe_by_token( $token ) ? esc_html__( 'You have been unsubscribed.', 'stockbeacon-product-waitlists-for-woocommerce' ) : esc_html__( 'Invalid unsubscribe link.', 'stockbeacon-product-waitlists-for-woocommerce' ) ) );
    }

    private function plugin_enabled() : bool {
        return false !== (Settings::get()['plugin_enabled'] ?? true);
    }

    private function get_readable_input_colors( string $background ) : array {
        $hex = ltrim( $background, '#' );
        if ( 6 !== strlen( $hex ) || !ctype_xdigit( $hex ) ) {
            return array('#0f172a', '#64748b');
        }
        $r = hexdec( substr( $hex, 0, 2 ) ) / 255;
        $g = hexdec( substr( $hex, 2, 2 ) ) / 255;
        $b = hexdec( substr( $hex, 4, 2 ) ) / 255;
        $convert = static function ( float $channel ) : float {
            return ( $channel <= 0.03928 ? $channel / 12.92 : (($channel + 0.055) / 1.055) ** 2.4 );
        };
        $luminance = 0.2126 * $convert( $r ) + 0.7151999999999999 * $convert( $g ) + 0.0722 * $convert( $b );
        return ( $luminance > 0.42 ? array('#0f172a', '#64748b') : array('#f8fafc', '#94a3b8') );
    }

    private function get_form_position_priority() : int {
        $position = sanitize_key( (string) (Settings::get()['form_display_position'] ?? 'after_price') );
        switch ( $position ) {
            case 'before_price':
                return 9;
            case 'after_price':
                return 11;
            case 'before_add_to_cart':
                return 29;
            case 'summary_end':
                return 55;
            case 'after_add_to_cart':
            default:
                return 31;
        }
    }

}
