<?php

namespace WPVOID\StockBeacon\Api;

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

    private Throttle $throttle;

    public function __construct( WaitlistManager $manager ) {
        $this->manager = $manager;
        $this->throttle = new Throttle();
    }

    public function register() : void {
        add_action( 'rest_api_init', function () {
            register_rest_route( 'stockbeacon/v1', '/subscribe', array(
                'methods'             => 'POST',
                'callback'            => array($this, 'subscribe'),
                'permission_callback' => array($this, 'can_subscribe'),
            ) );
            register_rest_route( 'stockbeacon/v1', '/confirm/(?P<token>[a-zA-Z0-9]+)', array(
                'methods'             => 'GET',
                'callback'            => array($this, 'confirm'),
                'permission_callback' => array($this, 'has_token_permission'),
            ) );
            register_rest_route( 'stockbeacon/v1', '/unsubscribe/(?P<token>[a-zA-Z0-9]+)', array(
                'methods'             => 'GET',
                'callback'            => array($this, 'unsubscribe'),
                'permission_callback' => array($this, 'has_token_permission'),
            ) );
            register_rest_route( 'stockbeacon/v1', '/overview', array(
                'methods'             => 'GET',
                'callback'            => array($this, 'overview'),
                'permission_callback' => array($this, 'can_manage'),
            ) );
            register_rest_route( 'stockbeacon/v1', '/waitlists', array(
                'methods'             => 'GET',
                'callback'            => array($this, 'waitlists'),
                'permission_callback' => array($this, 'can_manage'),
            ) );
            register_rest_route( 'stockbeacon/v1', '/subscribers', array(
                'methods'             => 'GET',
                'callback'            => array($this, 'recent_subscribers'),
                'permission_callback' => array($this, 'can_manage'),
            ) );
            register_rest_route( 'stockbeacon/v1', '/activity', array(
                'methods'             => 'GET',
                'callback'            => array($this, 'activity'),
                'permission_callback' => array($this, 'can_manage'),
            ) );
            register_rest_route( 'stockbeacon/v1', '/debug/status', array(
                'methods'             => 'GET',
                'callback'            => array($this, 'debug_status'),
                'permission_callback' => array($this, 'can_manage'),
            ) );
            register_rest_route( 'stockbeacon/v1', '/debug/test-batch', array(
                'methods'             => 'POST',
                'callback'            => array($this, 'debug_test_batch'),
                'permission_callback' => array($this, 'can_manage'),
            ) );
            register_rest_route( 'stockbeacon/v1', '/debug/seed-subscribers', array(array(
                'methods'             => 'POST',
                'callback'            => array($this, 'debug_seed_subscribers'),
                'permission_callback' => array($this, 'can_manage'),
            ), array(
                'methods'             => 'DELETE',
                'callback'            => array($this, 'debug_clear_test_subscribers'),
                'permission_callback' => array($this, 'can_manage'),
            )) );
            register_rest_route( 'stockbeacon/v1', '/waitlists/(?P<product_id>\\d+)', array(
                'methods'             => 'GET',
                'callback'            => array($this, 'waitlist_for_product'),
                'permission_callback' => array($this, 'can_manage'),
            ) );
            register_rest_route( 'stockbeacon/v1', '/waitlists/(?P<product_id>\\d+)', array(
                'methods'             => 'PUT',
                'callback'            => array($this, 'update_waitlist_product'),
                'permission_callback' => array($this, 'can_manage'),
            ) );
            register_rest_route( 'stockbeacon/v1', '/waitlists/(?P<product_id>\\d+)/subscribers', array(
                'methods'             => 'GET',
                'callback'            => array($this, 'subscribers'),
                'permission_callback' => array($this, 'can_manage'),
            ) );
            register_rest_route( 'stockbeacon/v1', '/waitlists/(?P<product_id>\\d+)', array(
                'methods'             => 'DELETE',
                'callback'            => array($this, 'delete_waitlist_for_product'),
                'permission_callback' => array($this, 'can_manage'),
            ) );
            register_rest_route( 'stockbeacon/v1', '/subscribers/(?P<id>\\d+)', array(
                'methods'             => 'DELETE',
                'callback'            => array($this, 'delete_subscriber'),
                'permission_callback' => array($this, 'can_manage'),
            ) );
        } );
    }

    public function can_subscribe( \WP_REST_Request $request ) {
        $nonce = (string) (( $request->get_param( '_wpnonce' ) ?: $request->get_header( 'X-WP-Nonce' ) ));
        if ( !wp_verify_nonce( $nonce, 'stockbeacon_subscribe' ) ) {
            return new \WP_Error('invalid_nonce', __( 'Invalid nonce.', 'stockbeacon-product-waitlists-for-woocommerce' ), array(
                'status' => 403,
            ));
        }
        return true;
    }

    public function has_token_permission( \WP_REST_Request $request ) {
        $token = sanitize_text_field( (string) $request['token'] );
        if ( !preg_match( '/\\A(?:[0-9a-f]{32}|[0-9a-f]{64})\\z/D', $token ) ) {
            return new \WP_Error('invalid_token', __( 'Invalid token.', 'stockbeacon-product-waitlists-for-woocommerce' ), array(
                'status' => 400,
            ));
        }
        return true;
    }

    public function subscribe( \WP_REST_Request $request ) {
        try {
            $nonce = (string) (( $request->get_param( '_wpnonce' ) ?: $request->get_header( 'X-WP-Nonce' ) ));
            if ( !wp_verify_nonce( $nonce, 'stockbeacon_subscribe' ) ) {
                return new \WP_Error('invalid_nonce', __( 'Invalid nonce.', 'stockbeacon-product-waitlists-for-woocommerce' ), array(
                    'status' => 403,
                ));
            }
            $email = SignupValidator::normalize_email( (string) $request->get_param( 'email' ) );
            $product_id = absint( $request->get_param( 'product_id' ) );
            $consent = absint( $request->get_param( 'consent' ) );
            if ( !is_email( $email ) || !$product_id ) {
                return new \WP_Error('invalid_input', __( 'Invalid input.', 'stockbeacon-product-waitlists-for-woocommerce' ), array(
                    'status' => 400,
                ));
            }
            if ( SignupValidator::consent_required() && !$consent ) {
                return new \WP_Error('consent_required', __( 'Please check the consent box.', 'stockbeacon-product-waitlists-for-woocommerce' ), array(
                    'status' => 400,
                ));
            }
            $variation_id = absint( $request->get_param( 'variation_id' ) );
            $eligible = SignupValidator::validate_subscribe_eligibility( $product_id, $variation_id );
            if ( is_wp_error( $eligible ) ) {
                return $eligible;
            }
            $settings = Settings::get();
            $ip = sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0' ) );
            $rate_per_min = (int) ($settings['throttle_per_minute'] ?? 5);
            if ( !$this->throttle->is_allowed( $ip, $rate_per_min ) ) {
                return new \WP_Error('rate_limited', __( 'Too many requests. Please wait and try again.', 'stockbeacon-product-waitlists-for-woocommerce' ), array(
                    'status' => 429,
                ));
            }
            $email_manager = new EmailManager($this->manager);
            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 );
                }
                return $this->idempotent_subscription_response();
            }
            $status = 'active';
            $token_raw = null;
            $token_hash = null;
            $product = null;
            $id = $this->manager->add_or_resubscribe( array(
                'email'        => $email,
                'product_id'   => $product_id,
                'variation_id' => $variation_id,
                'consent'      => $consent,
                'user_id'      => get_current_user_id(),
                'status'       => $status,
                'opt_in_token' => $token_hash,
                'ip_address'   => $ip,
            ) );
            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 );
                    }
                    return $this->idempotent_subscription_response();
                }
                return new \WP_Error('db_error', __( 'Unable to subscribe.', 'stockbeacon-product-waitlists-for-woocommerce' ), array(
                    'status' => 500,
                ));
            }
            $this->manager->bust_count_cache( $product_id );
            return $this->idempotent_subscription_response();
        } catch ( \Throwable $e ) {
            $this->log_exception( 'subscribe', $e );
            return new \WP_Error('subscribe_unavailable', __( 'Subscription service is temporarily unavailable.', 'stockbeacon-product-waitlists-for-woocommerce' ), array(
                'status' => 503,
            ));
        }
    }

    private function idempotent_subscription_response() : \WP_REST_Response {
        return rest_ensure_response( array(
            'success' => true,
            'message' => $this->subscription_received_message(),
        ) );
    }

    /**
     * Success message for signups. 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 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 overview( \WP_REST_Request $request ) : \WP_REST_Response {
        try {
            $search = sanitize_text_field( (string) $request->get_param( 'search' ) );
            $data = $this->manager->get_overview_snapshot( 20, $search );
            return rest_ensure_response( $data );
        } catch ( \Throwable $e ) {
            $this->log_exception( 'overview', $e );
            return rest_ensure_response( array(
                'total_active'   => 0,
                'total_notified' => 0,
                'products_count' => 0,
                'recent_signups' => array(),
            ) );
        }
    }

    public function activity( \WP_REST_Request $request ) : \WP_REST_Response {
        try {
            $page = max( 1, absint( $request->get_param( 'page' ) ) );
            $per_page = max( 1, min( 100, absint( ( $request->get_param( 'per_page' ) ?: 50 ) ) ) );
            $search = sanitize_text_field( (string) $request->get_param( 'search' ) );
            $type = sanitize_key( (string) $request->get_param( 'type' ) );
            $delivery = sanitize_key( (string) $request->get_param( 'delivery' ) );
            return rest_ensure_response( $this->manager->get_activity_logs(
                $page,
                $per_page,
                $search,
                $type,
                $delivery
            ) );
        } catch ( \Throwable $e ) {
            $this->log_exception( 'activity', $e );
            return rest_ensure_response( array() );
        }
    }

    public function recent_subscribers( \WP_REST_Request $request ) : \WP_REST_Response {
        try {
            $search = sanitize_text_field( (string) $request->get_param( 'search' ) );
            $status = sanitize_key( (string) $request->get_param( 'status' ) );
            $date_from = sanitize_text_field( (string) $request->get_param( 'date_from' ) );
            $date_to = sanitize_text_field( (string) $request->get_param( 'date_to' ) );
            $per_page = max( 1, min( 100, absint( ( $request->get_param( 'per_page' ) ?: 50 ) ) ) );
            return rest_ensure_response( $this->manager->get_recent_subscribers(
                $per_page,
                $search,
                $status,
                $date_from,
                $date_to
            ) );
        } catch ( \Throwable $e ) {
            $this->log_exception( 'recent_subscribers', $e );
            return rest_ensure_response( array() );
        }
    }

    public function debug_status( \WP_REST_Request $request ) : \WP_REST_Response {
        try {
            global $wpdb;
            $subscription_table = $wpdb->prefix . 'stockbeacon_subscriptions';
            $log_table = $wpdb->prefix . 'stockbeacon_logs';
            $scheduled_batches = 0;
            $disabled_functions = array_map( 'trim', explode( ',', (string) ini_get( 'disable_functions' ) ) );
            $php_mail_available = function_exists( 'mail' ) && !in_array( 'mail', $disabled_functions, true );
            $cron_array = _get_cron_array();
            if ( is_array( $cron_array ) ) {
                foreach ( $cron_array as $timestamp => $hooks ) {
                    if ( isset( $hooks['stockbeacon_send_restock_batch'] ) && is_array( $hooks['stockbeacon_send_restock_batch'] ) ) {
                        $scheduled_batches += count( $hooks['stockbeacon_send_restock_batch'] );
                    }
                }
            }
            $last_activity = $this->manager->get_activity_logs( 1, 1 );
            // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Prepared existence checks for the plugin-owned tables on the admin debug screen.
            $subscriptions_table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $subscription_table ) );
            $logs_table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $log_table ) );
            // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $status = array(
                'woocommerce_active'          => class_exists( 'WooCommerce' ),
                'subscriptions_table_ok'      => $subscription_table === $subscriptions_table_exists,
                'logs_table_ok'               => $log_table === $logs_table_exists,
                'wp_mail_available'           => function_exists( 'wp_mail' ),
                'php_mail_function_available' => $php_mail_available,
                'scheduled_batch_count'       => $scheduled_batches,
                'last_activity'               => $last_activity[0] ?? null,
            );
            return rest_ensure_response( $status );
        } catch ( \Throwable $e ) {
            $this->log_exception( 'debug_status', $e );
            return rest_ensure_response( array(
                'woocommerce_active'          => class_exists( 'WooCommerce' ),
                'subscriptions_table_ok'      => false,
                'logs_table_ok'               => false,
                'wp_mail_available'           => function_exists( 'wp_mail' ),
                'php_mail_function_available' => function_exists( 'mail' ),
                'scheduled_batch_count'       => 0,
                'last_activity'               => null,
            ) );
        }
    }

    public function debug_test_batch( \WP_REST_Request $request ) {
        $product_id = absint( $request->get_param( 'product_id' ) );
        $variation_id = absint( $request->get_param( 'variation_id' ) );
        if ( !$product_id ) {
            return new \WP_Error('invalid_product', __( 'Choose a product before scheduling a test batch.', 'stockbeacon-product-waitlists-for-woocommerce' ), array(
                'status' => 400,
            ));
        }
        wp_schedule_single_event( time() + MINUTE_IN_SECONDS, 'stockbeacon_send_restock_batch', array($product_id, $variation_id, 0) );
        return rest_ensure_response( array(
            'success' => true,
            'message' => __( 'A restock batch was scheduled for this product.', 'stockbeacon-product-waitlists-for-woocommerce' ),
        ) );
    }

    public function debug_seed_subscribers( \WP_REST_Request $request ) {
        $product_id = absint( $request->get_param( 'product_id' ) );
        $variation_id = absint( $request->get_param( 'variation_id' ) );
        $count = min( 10, max( 1, absint( ( $request->get_param( 'count' ) ?: 3 ) ) ) );
        if ( !$product_id ) {
            return new \WP_Error('invalid_product', __( 'Enter a product ID before seeding.', 'stockbeacon-product-waitlists-for-woocommerce' ), array(
                'status' => 400,
            ));
        }
        $created = array();
        for ($i = 1; $i <= $count; $i++) {
            $email = "test-{$i}@restock-test.invalid";
            try {
                $this->manager->add_or_resubscribe( array(
                    'email'        => $email,
                    'product_id'   => $product_id,
                    'variation_id' => $variation_id,
                    'user_id'      => 0,
                    'ip_address'   => '127.0.0.1',
                ) );
                $created[] = $email;
            } catch ( \Throwable $e ) {
                // Skip rows that cannot be inserted.
            }
        }
        return rest_ensure_response( array(
            'success' => true,
            'created' => $created,
            'message' => sprintf( 
                /* translators: %d: number of test subscribers added */
                _n(
                    'Added %d test subscriber.',
                    'Added %d test subscribers.',
                    count( $created ),
                    'stockbeacon-product-waitlists-for-woocommerce'
                ),
                count( $created )
             ),
        ) );
    }

    public function debug_clear_test_subscribers( \WP_REST_Request $request ) : \WP_REST_Response {
        global $wpdb;
        $table = esc_sql( $wpdb->prefix . 'stockbeacon_subscriptions' );
        // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Fully prepared; only the esc_sql()'d plugin-owned table name is interpolated, removes seeded test rows only.
        $deleted = (int) $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $table . ' WHERE email LIKE %s', '%@restock-test.invalid' ) );
        // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
        return rest_ensure_response( array(
            'success' => true,
            'deleted' => $deleted,
            'message' => sprintf( 
                /* translators: %d: number of test subscribers removed */
                _n(
                    'Removed %d test subscriber.',
                    'Removed %d test subscribers.',
                    $deleted,
                    'stockbeacon-product-waitlists-for-woocommerce'
                ),
                $deleted
             ),
        ) );
    }

    public function waitlists( \WP_REST_Request $request ) {
        $page = max( 1, absint( $request->get_param( 'page' ) ) );
        $per_page = max( 1, absint( ( $request->get_param( 'per_page' ) ?: 20 ) ) );
        $search = sanitize_text_field( (string) $request->get_param( 'search' ) );
        $mode = sanitize_key( (string) $request->get_param( 'mode' ) );
        try {
            if ( 'outofstock' === $mode ) {
                return rest_ensure_response( $this->manager->get_out_of_stock_products( $page, $per_page, $search ) );
            }
            return rest_ensure_response( $this->manager->get_products_with_waitlists( $page, $per_page, $search ) );
        } catch ( \Throwable $e ) {
            $this->log_exception( 'waitlists', $e );
            return rest_ensure_response( array() );
        }
    }

    public function confirm( \WP_REST_Request $request ) {
        try {
            $token = sanitize_text_field( (string) $request['token'] );
            if ( '' === $token ) {
                return new \WP_Error('invalid_token', __( 'Invalid token.', 'stockbeacon-product-waitlists-for-woocommerce' ), array(
                    'status' => 400,
                ));
            }
            $ok = $this->manager->activate_by_optin_hash( hash( 'sha256', $token ) );
            if ( !$ok ) {
                return new \WP_Error('invalid_token', __( 'Invalid or expired token.', 'stockbeacon-product-waitlists-for-woocommerce' ), array(
                    'status' => 400,
                ));
            }
            return rest_ensure_response( array(
                'success' => true,
            ) );
        } catch ( \Throwable $e ) {
            $this->log_exception( 'confirm', $e );
            return new \WP_Error('confirm_unavailable', __( 'Confirmation is temporarily unavailable.', 'stockbeacon-product-waitlists-for-woocommerce' ), array(
                'status' => 503,
            ));
        }
    }

    public function subscribers( \WP_REST_Request $request ) {
        try {
            $product_id = (int) $request['product_id'];
            $page = max( 1, absint( $request->get_param( 'page' ) ) );
            $per_page = max( 1, absint( ( $request->get_param( 'per_page' ) ?: 20 ) ) );
            return rest_ensure_response( $this->manager->get_subscribers( $product_id, $page, $per_page ) );
        } catch ( \Throwable $e ) {
            $this->log_exception( 'subscribers', $e );
            return rest_ensure_response( array() );
        }
    }

    public function unsubscribe( \WP_REST_Request $request ) : \WP_REST_Response {
        try {
            $token = sanitize_text_field( (string) $request['token'] );
            if ( '' === $token ) {
                return rest_ensure_response( array(
                    'success' => false,
                ) );
            }
            return rest_ensure_response( array(
                'success' => $this->manager->unsubscribe_by_token( $token ),
            ) );
        } catch ( \Throwable $e ) {
            $this->log_exception( 'unsubscribe', $e );
            return rest_ensure_response( array(
                'success' => false,
            ) );
        }
    }

    public function waitlist_for_product( \WP_REST_Request $request ) : \WP_REST_Response {
        try {
            return rest_ensure_response( $this->manager->get_for_product( (int) $request['product_id'], 100 ) );
        } catch ( \Throwable $e ) {
            $this->log_exception( 'waitlist_for_product', $e );
            return rest_ensure_response( array() );
        }
    }

    public function update_waitlist_product( \WP_REST_Request $request ) : \WP_REST_Response {
        try {
            $product_id = absint( $request['product_id'] );
            $product = ( $product_id > 0 ? wc_get_product( $product_id ) : null );
            if ( !$product instanceof \WC_Product ) {
                return new \WP_Error('invalid_product', __( 'Invalid product.', 'stockbeacon-product-waitlists-for-woocommerce' ), array(
                    'status' => 404,
                ));
            }
            $params = (array) $request->get_json_params();
            $visible = ( array_key_exists( 'waitlist_form_visible', $params ) ? (bool) $params['waitlist_form_visible'] : true );
            update_post_meta( $product_id, '_stockbeacon_hide_waitlist', ( $visible ? 'no' : 'yes' ) );
            return rest_ensure_response( $this->manager->get_waitlist_product_summary( $product_id ) );
        } catch ( \Throwable $e ) {
            $this->log_exception( 'update_waitlist_product', $e );
            return new \WP_Error('update_failed', __( 'Could not update the product waitlist setting.', 'stockbeacon-product-waitlists-for-woocommerce' ), array(
                'status' => 500,
            ));
        }
    }

    public function delete_waitlist_for_product( \WP_REST_Request $request ) : \WP_REST_Response {
        try {
            return rest_ensure_response( array(
                'deleted' => $this->manager->clear_product( (int) $request['product_id'] ),
            ) );
        } catch ( \Throwable $e ) {
            $this->log_exception( 'delete_waitlist_for_product', $e );
            return rest_ensure_response( array(
                'deleted' => 0,
            ) );
        }
    }

    public function delete_subscriber( \WP_REST_Request $request ) : \WP_REST_Response {
        try {
            return rest_ensure_response( array(
                'deleted' => $this->manager->delete_subscription( (int) $request['id'] ),
            ) );
        } catch ( \Throwable $e ) {
            $this->log_exception( 'delete_subscriber', $e );
            return rest_ensure_response( array(
                'deleted' => false,
            ) );
        }
    }

}
