<?php
// phpcs:disable WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.InputNotValidated
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange
if ( ! defined( 'ABSPATH' ) ) exit;

class YATOON_Growth_Modules {

    public static function init() {
        add_action( 'admin_init', array( __CLASS__, 'register_settings' ) );
        add_action( 'admin_menu', array( __CLASS__, 'add_menu' ), 30 );
        add_action( 'init', array( __CLASS__, 'maybe_create_tables' ), 5 );
        add_action( 'admin_post_yatoon_save_growth_module', array( __CLASS__, 'handle_save' ) );
        add_action( 'admin_post_yatoon_growth_activate_purchase', array( __CLASS__, 'handle_activate_purchase' ) );
        add_action( 'wp_ajax_yatoon_validate_coupon', array( __CLASS__, 'ajax_validate_coupon' ) );
        add_action( 'wp_ajax_nopriv_yatoon_validate_coupon', array( __CLASS__, 'ajax_validate_coupon' ) );
    }

    public static function register_settings() {
        register_setting( 'yatoon_growth_settings', 'yatoon_enable_coupons', array( 'sanitize_callback' => 'sanitize_text_field' ) );
        register_setting( 'yatoon_growth_settings', 'yatoon_enable_packages', array( 'sanitize_callback' => 'sanitize_text_field' ) );
        register_setting( 'yatoon_growth_settings', 'yatoon_enable_memberships', array( 'sanitize_callback' => 'sanitize_text_field' ) );
    }

    public static function add_menu() {
        add_submenu_page(
            'yatoon-booking-system',
            'Coupons, Packages & Memberships',
            'Growth Modules',
            'manage_options',
            'yatoon-growth-modules',
            array( __CLASS__, 'render_page' )
        );
    }

    public static function is_enabled( $module ) {
        if ( ! self::is_pro_feature_available() ) {
            return false;
        }

        return get_option( 'yatoon_enable_' . $module, '0' ) === '1';
    }

    private static function is_pro_feature_available() {
        return class_exists( 'YATOON_License' ) && YATOON_License::is_pro();
    }

    public static function maybe_create_tables() {
        global $wpdb;
        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        $charset = $wpdb->get_charset_collate();

        dbDelta( "CREATE TABLE {$wpdb->prefix}yatoon_coupons (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            code varchar(50) NOT NULL,
            description varchar(255) DEFAULT '',
            discount_type varchar(20) NOT NULL DEFAULT 'percent',
            amount decimal(10,2) NOT NULL DEFAULT 0.00,
            usage_limit int(11) DEFAULT 0,
            used_count int(11) DEFAULT 0,
            starts_at date DEFAULT NULL,
            expires_at date DEFAULT NULL,
            is_active tinyint(1) NOT NULL DEFAULT 1,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY  (id),
            UNIQUE KEY code (code)
        ) $charset;" );

        dbDelta( "CREATE TABLE {$wpdb->prefix}yatoon_packages (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            name varchar(120) NOT NULL,
            description text DEFAULT NULL,
            included_visits int(11) NOT NULL DEFAULT 1,
            price decimal(10,2) NOT NULL DEFAULT 0.00,
            validity_days int(11) NOT NULL DEFAULT 90,
            is_active tinyint(1) NOT NULL DEFAULT 1,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY  (id)
        ) $charset;" );

        dbDelta( "CREATE TABLE {$wpdb->prefix}yatoon_memberships (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            name varchar(120) NOT NULL,
            description text DEFAULT NULL,
            billing_period varchar(20) NOT NULL DEFAULT 'monthly',
            price decimal(10,2) NOT NULL DEFAULT 0.00,
            booking_discount_percent decimal(5,2) NOT NULL DEFAULT 0.00,
            is_active tinyint(1) NOT NULL DEFAULT 1,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY  (id)
        ) $charset;" );

        dbDelta( "CREATE TABLE {$wpdb->prefix}yatoon_customer_packages (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            package_id bigint(20) unsigned NOT NULL,
            customer_name varchar(255) DEFAULT '',
            customer_email varchar(255) DEFAULT '',
            customer_phone varchar(50) DEFAULT '',
            purchased_visits int(11) NOT NULL DEFAULT 1,
            remaining_visits int(11) NOT NULL DEFAULT 1,
            paid_amount decimal(10,2) NOT NULL DEFAULT 0.00,
            starts_at date DEFAULT NULL,
            expires_at date DEFAULT NULL,
            status varchar(20) NOT NULL DEFAULT 'active',
            notes text DEFAULT NULL,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            updated_at datetime DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY  (id),
            KEY customer_email (customer_email),
            KEY customer_phone (customer_phone),
            KEY package_id (package_id),
            KEY status (status)
        ) $charset;" );

        dbDelta( "CREATE TABLE {$wpdb->prefix}yatoon_customer_memberships (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            membership_id bigint(20) unsigned NOT NULL,
            customer_name varchar(255) DEFAULT '',
            customer_email varchar(255) DEFAULT '',
            customer_phone varchar(50) DEFAULT '',
            paid_amount decimal(10,2) NOT NULL DEFAULT 0.00,
            starts_at date DEFAULT NULL,
            expires_at date DEFAULT NULL,
            status varchar(20) NOT NULL DEFAULT 'active',
            notes text DEFAULT NULL,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            updated_at datetime DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY  (id),
            KEY customer_email (customer_email),
            KEY customer_phone (customer_phone),
            KEY membership_id (membership_id),
            KEY status (status)
        ) $charset;" );

        dbDelta( "CREATE TABLE {$wpdb->prefix}yatoon_growth_redemptions (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            booking_id bigint(20) unsigned NOT NULL DEFAULT 0,
            module_type varchar(20) NOT NULL,
            customer_package_id bigint(20) unsigned DEFAULT NULL,
            customer_membership_id bigint(20) unsigned DEFAULT NULL,
            amount decimal(10,2) NOT NULL DEFAULT 0.00,
            visits_used int(11) NOT NULL DEFAULT 0,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY  (id),
            KEY booking_id (booking_id),
            KEY module_type (module_type),
            KEY customer_package_id (customer_package_id),
            KEY customer_membership_id (customer_membership_id),
            KEY created_at (created_at)
        ) $charset;" );
    }

    public static function handle_save() {
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( 'Permission denied' );
        }
        check_admin_referer( 'yatoon_growth_save' );

        if ( ! self::is_pro_feature_available() ) {
            wp_safe_redirect( admin_url( 'admin.php?page=yatoon-growth-modules&pro_required=1' ) );
            exit;
        }

        $type = isset( $_POST['module_type'] ) ? sanitize_key( wp_unslash( $_POST['module_type'] ) ) : '';

        // Only the master "Module Switches" form is allowed to mutate the
        // enabled flags. Sub-forms (add coupon / package / membership / grant)
        // must NEVER overwrite the switches -- this prevents a missing hidden
        // field (e.g. dropped by a browser extension) from silently disabling
        // a module the admin is actively configuring.
        if ( $type === '' ) {
            update_option( 'yatoon_enable_coupons',     ! empty( $_POST['yatoon_enable_coupons'] )     ? '1' : '0' );
            update_option( 'yatoon_enable_packages',    ! empty( $_POST['yatoon_enable_packages'] )    ? '1' : '0' );
            update_option( 'yatoon_enable_memberships', ! empty( $_POST['yatoon_enable_memberships'] ) ? '1' : '0' );
        }
        if ( $type === 'coupon' ) {
            self::save_coupon();
        } elseif ( $type === 'package' ) {
            self::save_package();
        } elseif ( $type === 'membership' ) {
            self::save_membership();
        } elseif ( $type === 'grant_package' ) {
            self::grant_package();
        } elseif ( $type === 'grant_membership' ) {
            self::grant_membership();
        }

        wp_safe_redirect( admin_url( 'admin.php?page=yatoon-growth-modules&saved=1' ) );
        exit;
    }

    private static function save_coupon() {
        global $wpdb;
        $code = strtoupper( sanitize_text_field( wp_unslash( $_POST['coupon_code'] ?? '' ) ) );
        if ( $code === '' ) return;
        $data = array(
            'code'          => $code,
            'description'   => sanitize_text_field( wp_unslash( $_POST['coupon_description'] ?? '' ) ),
            'discount_type' => in_array( $_POST['coupon_discount_type'] ?? '', array( 'percent', 'fixed' ), true ) ? sanitize_key( $_POST['coupon_discount_type'] ) : 'percent',
            'amount'        => max( 0, floatval( $_POST['coupon_amount'] ?? 0 ) ),
            'usage_limit'   => max( 0, intval( $_POST['coupon_usage_limit'] ?? 0 ) ),
            'starts_at'     => sanitize_text_field( wp_unslash( $_POST['coupon_starts_at'] ?? '' ) ) ?: null,
            'expires_at'    => sanitize_text_field( wp_unslash( $_POST['coupon_expires_at'] ?? '' ) ) ?: null,
            'is_active'     => ! empty( $_POST['coupon_is_active'] ) ? 1 : 0,
        );
        $wpdb->replace( $wpdb->prefix . 'yatoon_coupons', $data );
    }

    private static function save_package() {
        global $wpdb;
        $name = sanitize_text_field( wp_unslash( $_POST['package_name'] ?? '' ) );
        if ( $name === '' ) return;
        $wpdb->insert( $wpdb->prefix . 'yatoon_packages', array(
            'name'            => $name,
            'description'     => sanitize_textarea_field( wp_unslash( $_POST['package_description'] ?? '' ) ),
            'included_visits' => max( 1, intval( $_POST['package_visits'] ?? 1 ) ),
            'price'           => max( 0, floatval( $_POST['package_price'] ?? 0 ) ),
            'validity_days'   => max( 1, intval( $_POST['package_validity_days'] ?? 90 ) ),
            'is_active'       => ! empty( $_POST['package_is_active'] ) ? 1 : 0,
        ) );
    }

    private static function save_membership() {
        global $wpdb;
        $name = sanitize_text_field( wp_unslash( $_POST['membership_name'] ?? '' ) );
        if ( $name === '' ) return;
        $period = in_array( $_POST['membership_period'] ?? '', array( 'monthly', 'yearly' ), true ) ? sanitize_key( $_POST['membership_period'] ) : 'monthly';
        $wpdb->insert( $wpdb->prefix . 'yatoon_memberships', array(
            'name'                     => $name,
            'description'              => sanitize_textarea_field( wp_unslash( $_POST['membership_description'] ?? '' ) ),
            'billing_period'           => $period,
            'price'                    => max( 0, floatval( $_POST['membership_price'] ?? 0 ) ),
            'booking_discount_percent' => min( 100, max( 0, floatval( $_POST['membership_discount'] ?? 0 ) ) ),
            'is_active'                => ! empty( $_POST['membership_is_active'] ) ? 1 : 0,
        ) );
    }

    private static function grant_package() {
        global $wpdb;
        $package_id = absint( $_POST['customer_package_id'] ?? 0 );
        $package = $package_id ? $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}yatoon_packages WHERE id = %d AND is_active = 1",
            $package_id
        ) ) : null;
        if ( ! $package ) return;

        $visits = max( 1, intval( $_POST['customer_package_visits'] ?? $package->included_visits ) );
        $starts = sanitize_text_field( wp_unslash( $_POST['customer_package_starts_at'] ?? '' ) );
        if ( $starts === '' ) $starts = current_time( 'Y-m-d' );
        $expires = sanitize_text_field( wp_unslash( $_POST['customer_package_expires_at'] ?? '' ) );
        if ( $expires === '' ) {
            $expires = gmdate( 'Y-m-d', strtotime( $starts . ' +' . max( 1, intval( $package->validity_days ) ) . ' days' ) );
        }

        $wpdb->insert( $wpdb->prefix . 'yatoon_customer_packages', array(
            'package_id'       => $package_id,
            'customer_name'    => sanitize_text_field( wp_unslash( $_POST['customer_name'] ?? '' ) ),
            'customer_email'   => sanitize_email( wp_unslash( $_POST['customer_email'] ?? '' ) ),
            'customer_phone'   => sanitize_text_field( wp_unslash( $_POST['customer_phone'] ?? '' ) ),
            'purchased_visits' => $visits,
            'remaining_visits' => $visits,
            'paid_amount'      => max( 0, floatval( $_POST['customer_package_paid_amount'] ?? $package->price ) ),
            'starts_at'        => $starts,
            'expires_at'       => $expires,
            'status'           => 'active',
            'notes'            => sanitize_textarea_field( wp_unslash( $_POST['customer_notes'] ?? '' ) ),
        ) );
    }

    private static function grant_membership() {
        global $wpdb;
        $membership_id = absint( $_POST['customer_membership_id'] ?? 0 );
        $membership = $membership_id ? $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}yatoon_memberships WHERE id = %d AND is_active = 1",
            $membership_id
        ) ) : null;
        if ( ! $membership ) return;

        $starts = sanitize_text_field( wp_unslash( $_POST['customer_membership_starts_at'] ?? '' ) );
        if ( $starts === '' ) $starts = current_time( 'Y-m-d' );
        $expires = sanitize_text_field( wp_unslash( $_POST['customer_membership_expires_at'] ?? '' ) );
        if ( $expires === '' ) {
            $days = ( $membership->billing_period === 'yearly' ) ? 365 : 30;
            $expires = gmdate( 'Y-m-d', strtotime( $starts . ' +' . $days . ' days' ) );
        }

        $wpdb->insert( $wpdb->prefix . 'yatoon_customer_memberships', array(
            'membership_id'  => $membership_id,
            'customer_name'  => sanitize_text_field( wp_unslash( $_POST['customer_name'] ?? '' ) ),
            'customer_email' => sanitize_email( wp_unslash( $_POST['customer_email'] ?? '' ) ),
            'customer_phone' => sanitize_text_field( wp_unslash( $_POST['customer_phone'] ?? '' ) ),
            'paid_amount'    => max( 0, floatval( $_POST['customer_membership_paid_amount'] ?? $membership->price ) ),
            'starts_at'      => $starts,
            'expires_at'     => $expires,
            'status'         => 'active',
            'notes'          => sanitize_textarea_field( wp_unslash( $_POST['customer_notes'] ?? '' ) ),
        ) );
    }

    public static function get_coupon_discount( $code, $total ) {
        if ( ! self::is_enabled( 'coupons' ) || $code === '' || $total <= 0 ) {
            return array( 'valid' => false, 'discount' => 0, 'message' => '' );
        }
        global $wpdb;
        $coupon = $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}yatoon_coupons WHERE code = %s AND is_active = 1",
            strtoupper( $code )
        ) );
        if ( ! $coupon ) {
            return array( 'valid' => false, 'discount' => 0, 'message' => __( 'Coupon code is invalid.', 'yatoon-booking-system' ) );
        }
        $today = current_time( 'Y-m-d' );
        if ( ( $coupon->starts_at && $coupon->starts_at > $today ) || ( $coupon->expires_at && $coupon->expires_at < $today ) ) {
            return array( 'valid' => false, 'discount' => 0, 'message' => __( 'Coupon code is not active for this date.', 'yatoon-booking-system' ) );
        }
        if ( intval( $coupon->usage_limit ) > 0 && intval( $coupon->used_count ) >= intval( $coupon->usage_limit ) ) {
            return array( 'valid' => false, 'discount' => 0, 'message' => __( 'Coupon usage limit has been reached.', 'yatoon-booking-system' ) );
        }
        $discount = ( $coupon->discount_type === 'fixed' ) ? floatval( $coupon->amount ) : ( $total * floatval( $coupon->amount ) / 100 );
        $discount = min( $total, max( 0, round( $discount, 2 ) ) );
        return array( 'valid' => true, 'discount' => $discount, 'coupon' => $coupon, 'message' => __( 'Coupon applied.', 'yatoon-booking-system' ) );
    }

    public static function apply_coupon_to_total( $total, $code ) {
        $result = self::get_coupon_discount( $code, $total );
        if ( empty( $result['valid'] ) ) {
            return array( 'total' => $total, 'discount' => 0, 'code' => '' );
        }
        return array(
            'total'    => max( 0, round( $total - $result['discount'], 2 ) ),
            'discount' => $result['discount'],
            'code'     => strtoupper( $code ),
        );
    }

    /**
     * Atomically claim a coupon for use. Increments used_count only when the
     * coupon is still active, within its date window, and below its usage limit.
     * Should be called once per booking submission BEFORE the booking row is
     * inserted, so concurrent bookings cannot exceed the configured limit.
     *
     * @param string $code Coupon code (case-insensitive).
     * @return object|null  The claimed coupon row on success, null on failure.
     */
    public static function claim_coupon( $code ) {
        if ( ! self::is_enabled( 'coupons' ) || $code === '' ) {
            return null;
        }
        global $wpdb;
        $code  = strtoupper( $code );
        $today = current_time( 'Y-m-d' );

        // Atomic: only increment if active AND in date window AND under limit.
        // affected_rows === 1 proves we hold a single, reserved use.
        $rows = $wpdb->query( $wpdb->prepare(
            "UPDATE {$wpdb->prefix}yatoon_coupons
             SET used_count = used_count + 1
             WHERE code = %s
               AND is_active = 1
               AND ( starts_at IS NULL OR starts_at <= %s )
               AND ( expires_at IS NULL OR expires_at >= %s )
               AND ( usage_limit = 0 OR used_count < usage_limit )",
            $code, $today, $today
        ) );
        if ( intval( $rows ) !== 1 ) {
            return null;
        }
        return $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}yatoon_coupons WHERE code = %s",
            $code
        ) );
    }

    /**
     * Release a previously-claimed coupon (e.g. when the booking insert fails
     * after the coupon was claimed). Decrements used_count but never below 0.
     */
    public static function release_coupon( $code ) {
        if ( $code === '' ) return;
        global $wpdb;
        $wpdb->query( $wpdb->prepare(
            "UPDATE {$wpdb->prefix}yatoon_coupons
             SET used_count = GREATEST( used_count - 1, 0 )
             WHERE code = %s",
            strtoupper( $code )
        ) );
    }

    /**
     * @deprecated 4.5.14 Use claim_coupon() before inserting the booking row.
     * Kept as a safe no-op alias so existing call sites keep working during the
     * refactor - claim_coupon() already increments used_count atomically.
     */
    public static function mark_coupon_used( $code ) {
        // Intentional no-op: claim_coupon() already incremented used_count.
        // Kept for backwards compatibility. Do not re-increment here.
        unset( $code );
    }

    /**
     * Compute the discount a claimed coupon row would apply to a given total.
     * Mirrors the math in get_coupon_discount() but takes a coupon row directly
     * so we don't re-query the DB for every line in a multi-service booking.
     */
    public static function compute_coupon_discount_for( $coupon, $total ) {
        if ( ! $coupon || $total <= 0 ) return 0.0;
        $discount = ( $coupon->discount_type === 'fixed' )
            ? floatval( $coupon->amount )
            : ( $total * floatval( $coupon->amount ) / 100 );
        return min( $total, max( 0, round( $discount, 2 ) ) );
    }

    /**
     * Build a customer-match SQL fragment for package / membership lookups.
     *
     * Security: when both email AND phone are provided, BOTH must match the
     * granted record. This prevents a stranger from redeeming someone else's
     * package by guessing or learning a single piece of contact info.
     * Falls back to whichever one is provided when only one is available
     * (e.g. older imports), but the redemption flow always submits both.
     */
    private static function customer_match_sql( $email, $phone ) {
        $clauses = array();
        $values  = array();
        if ( $email !== '' && $phone !== '' ) {
            // Strict: both must match the same row.
            $clauses[] = 'customer_email = %s';
            $values[]  = $email;
            $clauses[] = 'customer_phone = %s';
            $values[]  = $phone;
            return array( 'sql' => '(' . implode( ' AND ', $clauses ) . ')', 'values' => $values );
        }
        if ( $email !== '' ) {
            $clauses[] = 'customer_email = %s';
            $values[]  = $email;
        }
        if ( $phone !== '' ) {
            $clauses[] = 'customer_phone = %s';
            $values[]  = $phone;
        }
        if ( empty( $clauses ) ) {
            return array( 'sql' => '1=0', 'values' => array() );
        }
        return array( 'sql' => '(' . implode( ' OR ', $clauses ) . ')', 'values' => $values );
    }

    public static function get_active_customer_package( $email, $phone ) {
        if ( ! self::is_enabled( 'packages' ) ) return null;
        global $wpdb;
        $email = sanitize_email( $email );
        $phone = sanitize_text_field( $phone );
        $match = self::customer_match_sql( $email, $phone );
        $today = current_time( 'Y-m-d' );
        $sql = "SELECT cp.*, p.name AS package_name
                FROM {$wpdb->prefix}yatoon_customer_packages cp
                LEFT JOIN {$wpdb->prefix}yatoon_packages p ON cp.package_id = p.id
                WHERE {$match['sql']}
                  AND cp.status = 'active'
                  AND cp.remaining_visits > 0
                  AND (cp.starts_at IS NULL OR cp.starts_at <= %s)
                  AND (cp.expires_at IS NULL OR cp.expires_at >= %s)
                ORDER BY cp.expires_at ASC, cp.id ASC
                LIMIT 1";
        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
        return $wpdb->get_row( $wpdb->prepare( $sql, array_merge( $match['values'], array( $today, $today ) ) ) );
    }

    public static function get_active_customer_membership( $email, $phone ) {
        if ( ! self::is_enabled( 'memberships' ) ) return null;
        global $wpdb;
        $email = sanitize_email( $email );
        $phone = sanitize_text_field( $phone );
        $match = self::customer_match_sql( $email, $phone );
        $today = current_time( 'Y-m-d' );
        $sql = "SELECT cm.*, m.name AS membership_name, m.booking_discount_percent
                FROM {$wpdb->prefix}yatoon_customer_memberships cm
                LEFT JOIN {$wpdb->prefix}yatoon_memberships m ON cm.membership_id = m.id
                WHERE {$match['sql']}
                  AND cm.status = 'active'
                  AND (cm.starts_at IS NULL OR cm.starts_at <= %s)
                  AND (cm.expires_at IS NULL OR cm.expires_at >= %s)
                ORDER BY cm.expires_at DESC, cm.id DESC
                LIMIT 1";
        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
        return $wpdb->get_row( $wpdb->prepare( $sql, array_merge( $match['values'], array( $today, $today ) ) ) );
    }

    /**
     * Reserve & apply customer benefits (package visit or membership discount)
     * to a single service line item.
     *
     * IMPORTANT: this method now performs an ATOMIC reservation when a package
     * is matched. The package's remaining_visits counter is decremented in the
     * same SQL statement that checks it is still > 0, so two concurrent
     * bookings can never both consume the same last visit. If the atomic
     * decrement fails (race lost) we fall back to the membership discount or
     * the original price.
     *
     * The $reserved_packages parameter is kept for API compatibility but is no
     * longer needed for correctness - the DB itself is the source of truth.
     */
    public static function apply_customer_benefits_to_total( $total, $email, $phone, &$reserved_packages = null ) {
        global $wpdb;
        $total = max( 0, round( floatval( $total ), 2 ) );
        $package = self::get_active_customer_package( $email, $phone );
        if ( $package ) {
            // Atomic reserve: only succeed if there is still a visit to consume.
            $reserved = $wpdb->query( $wpdb->prepare(
                "UPDATE {$wpdb->prefix}yatoon_customer_packages
                 SET remaining_visits = remaining_visits - 1, updated_at = %s
                 WHERE id = %d AND remaining_visits > 0 AND status = 'active'
                   AND ( starts_at IS NULL OR starts_at <= %s )
                   AND ( expires_at IS NULL OR expires_at >= %s )",
                current_time( 'mysql' ),
                intval( $package->id ),
                current_time( 'Y-m-d' ),
                current_time( 'Y-m-d' )
            ) );
            if ( intval( $reserved ) === 1 ) {
                if ( is_array( $reserved_packages ) ) {
                    $reserved_packages[ intval( $package->id ) ] = intval( $reserved_packages[ intval( $package->id ) ] ?? 0 ) + 1;
                }
                return array(
                    'total'         => 0,
                    'discount'      => $total,
                    'note'          => sprintf( 'Package visit applied: %s (%d remaining after booking).', $package->package_name ?: 'Package', max( 0, intval( $package->remaining_visits ) - 1 ) ),
                    'package_id'    => intval( $package->id ),
                    'membership_id' => 0,
                    'type'          => 'package',
                );
            }
            // race lost (someone else just consumed the last visit) - fall through
        }
        $membership = self::get_active_customer_membership( $email, $phone );
        $percent = $membership ? min( 100, max( 0, floatval( $membership->booking_discount_percent ) ) ) : 0;
        if ( $membership && $percent > 0 && $total > 0 ) {
            $discount = min( $total, round( $total * $percent / 100, 2 ) );
            return array(
                'total'      => max( 0, round( $total - $discount, 2 ) ),
                'discount'   => $discount,
                'note'       => sprintf( 'Membership %s applied: -%s%%.', $membership->membership_name ?: 'discount', rtrim( rtrim( number_format( $percent, 2 ), '0' ), '.' ) ),
                'package_id' => 0,
                'membership_id' => intval( $membership->id ),
                'type'       => 'membership',
            );
        }

        return array( 'total' => $total, 'discount' => 0, 'note' => '', 'package_id' => 0, 'membership_id' => 0, 'type' => '' );
    }

    /**
     * Release a previously-reserved package visit. Used when the booking row
     * insert fails AFTER apply_customer_benefits_to_total() reserved the visit
     * - without this, the visit would be silently lost.
     */
    public static function release_customer_benefit( $benefit ) {
        if ( empty( $benefit['type'] ) || $benefit['type'] !== 'package' || empty( $benefit['package_id'] ) ) {
            return;
        }
        global $wpdb;
        $wpdb->query( $wpdb->prepare(
            "UPDATE {$wpdb->prefix}yatoon_customer_packages
             SET remaining_visits = remaining_visits + 1, updated_at = %s
             WHERE id = %d",
            current_time( 'mysql' ),
            intval( $benefit['package_id'] )
        ) );
    }

    /**
     * Persist a redemption audit row. The visit balance was already decremented
     * atomically in apply_customer_benefits_to_total(), so this method now only
     * writes to the redemptions log - preventing the previous double-decrement
     * bug where the visit was implicitly consumed twice.
     */
    public static function record_booking_benefit( $benefit, $booking_id ) {
        if ( empty( $benefit['type'] ) || ! $booking_id ) return;
        global $wpdb;
        if ( $benefit['type'] === 'package' && ! empty( $benefit['package_id'] ) ) {
            $wpdb->insert( $wpdb->prefix . 'yatoon_growth_redemptions', array(
                'booking_id'           => intval( $booking_id ),
                'module_type'          => 'package',
                'customer_package_id'  => intval( $benefit['package_id'] ),
                'amount'               => max( 0, floatval( $benefit['discount'] ?? 0 ) ),
                'visits_used'          => 1,
            ) );
        } elseif ( $benefit['type'] === 'membership' && ! empty( $benefit['membership_id'] ) ) {
            $wpdb->insert( $wpdb->prefix . 'yatoon_growth_redemptions', array(
                'booking_id'              => intval( $booking_id ),
                'module_type'             => 'membership',
                'customer_membership_id'  => intval( $benefit['membership_id'] ),
                'amount'                  => max( 0, floatval( $benefit['discount'] ?? 0 ) ),
                'visits_used'             => 0,
            ) );
        }
    }

    /**
     * Preview-only equivalent of apply_customer_benefits_to_total() - returns
     * the discount the customer WOULD receive without consuming any package
     * visits. Used by Stripe to compute a deposit amount that reflects the
     * actual amount the customer will owe at the salon.
     */
    public static function preview_customer_benefits_for_total( $total, $email, $phone ) {
        $total   = max( 0, round( floatval( $total ), 2 ) );
        $package = self::get_active_customer_package( $email, $phone );
        if ( $package && intval( $package->remaining_visits ) > 0 ) {
            return array( 'total' => 0, 'discount' => $total, 'type' => 'package' );
        }
        $membership = self::get_active_customer_membership( $email, $phone );
        $percent = $membership ? min( 100, max( 0, floatval( $membership->booking_discount_percent ) ) ) : 0;
        if ( $membership && $percent > 0 && $total > 0 ) {
            $discount = min( $total, round( $total * $percent / 100, 2 ) );
            return array( 'total' => max( 0, round( $total - $discount, 2 ) ), 'discount' => $discount, 'type' => 'membership' );
        }
        return array( 'total' => $total, 'discount' => 0, 'type' => '' );
    }

    public static function ajax_validate_coupon() {
        check_ajax_referer( 'yatoon_frontend_nonce', 'nonce' );

        if ( ! self::is_pro_feature_available() ) {
            wp_send_json_error( array( 'message' => __( 'Coupons require an active Yatoon Pro license.', 'yatoon-booking-system' ) ) );
        }

        $code  = sanitize_text_field( wp_unslash( $_POST['coupon_code'] ?? '' ) );
        $total = max( 0, floatval( $_POST['total'] ?? 0 ) );
        $result = self::get_coupon_discount( $code, $total );
        if ( empty( $result['valid'] ) ) {
            wp_send_json_error( array( 'message' => $result['message'] ?: __( 'Coupon could not be applied.', 'yatoon-booking-system' ) ) );
        }
        wp_send_json_success( array(
            'message'  => $result['message'],
            'discount' => $result['discount'],
            'total'    => max( 0, round( $total - $result['discount'], 2 ) ),
        ) );
    }

    public static function render_page() {
        if ( ! current_user_can( 'manage_options' ) ) return;

        if ( ! self::is_pro_feature_available() ) {
            if ( class_exists( 'YATOON_Admin' ) ) {
                YATOON_Admin::render_pro_gate(
                    'Growth Modules',
                    'Activate your Yatoon Pro license to use coupons, service packages, memberships, storefront purchases, and automatic benefit redemption.'
                );
            } else {
                echo '<div class="wrap"><h1>Growth Modules</h1><p>An active Yatoon Pro license is required.</p></div>';
            }
            return;
        }

        global $wpdb;
        $coupons = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}yatoon_coupons ORDER BY id DESC LIMIT 20" );
        $packages = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}yatoon_packages ORDER BY id DESC LIMIT 20" );
        $memberships = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}yatoon_memberships ORDER BY id DESC LIMIT 20" );
        $customer_packages = $wpdb->get_results( "SELECT cp.*, p.name AS package_name FROM {$wpdb->prefix}yatoon_customer_packages cp LEFT JOIN {$wpdb->prefix}yatoon_packages p ON cp.package_id = p.id ORDER BY cp.id DESC LIMIT 20" );
        $customer_memberships = $wpdb->get_results( "SELECT cm.*, m.name AS membership_name FROM {$wpdb->prefix}yatoon_customer_memberships cm LEFT JOIN {$wpdb->prefix}yatoon_memberships m ON cm.membership_id = m.id ORDER BY cm.id DESC LIMIT 20" );

        // Pending storefront purchases (admin needs to manually mark active in
        // request-mode, or just see them in Stripe-mode for traceability).
        $pending_packages = $wpdb->get_results(
            "SELECT cp.*, p.name AS package_name FROM {$wpdb->prefix}yatoon_customer_packages cp
             LEFT JOIN {$wpdb->prefix}yatoon_packages p ON cp.package_id = p.id
             WHERE cp.status = 'pending' ORDER BY cp.id DESC LIMIT 10"
        );
        $pending_memberships = $wpdb->get_results(
            "SELECT cm.*, m.name AS membership_name FROM {$wpdb->prefix}yatoon_customer_memberships cm
             LEFT JOIN {$wpdb->prefix}yatoon_memberships m ON cm.membership_id = m.id
             WHERE cm.status = 'pending' ORDER BY cm.id DESC LIMIT 10"
        );
        ?>
        <div class="wrap">
            <h1><?php esc_html_e( 'Growth Modules', 'yatoon-booking-system' ); ?></h1>
            <?php if ( isset( $_GET['saved'] ) ) : ?><div class="notice notice-success"><p><?php esc_html_e( 'Settings saved.', 'yatoon-booking-system' ); ?></p></div><?php endif; ?>
            <?php if ( isset( $_GET['activated'] ) ) : ?><div class="notice notice-success"><p><?php esc_html_e( 'Purchase activated.', 'yatoon-booking-system' ); ?></p></div><?php endif; ?>
            <?php self::render_reports_section(); ?>
            <?php if ( ! empty( $pending_packages ) || ! empty( $pending_memberships ) ) self::render_pending_section( $pending_packages, $pending_memberships ); ?>
            <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" style="background:#fff;border:1px solid #dcdcde;padding:16px;margin:16px 0;">
                <?php wp_nonce_field( 'yatoon_growth_save' ); ?>
                <input type="hidden" name="action" value="yatoon_save_growth_module">
                <h2><?php esc_html_e( 'Module Switches', 'yatoon-booking-system' ); ?></h2>
                <label><input type="checkbox" name="yatoon_enable_coupons" value="1" <?php checked( self::is_enabled( 'coupons' ) ); ?>> <?php esc_html_e( 'Enable coupons', 'yatoon-booking-system' ); ?></label><br>
                <label><input type="checkbox" name="yatoon_enable_packages" value="1" <?php checked( self::is_enabled( 'packages' ) ); ?>> <?php esc_html_e( 'Enable packages', 'yatoon-booking-system' ); ?></label><br>
                <label><input type="checkbox" name="yatoon_enable_memberships" value="1" <?php checked( self::is_enabled( 'memberships' ) ); ?>> <?php esc_html_e( 'Enable memberships', 'yatoon-booking-system' ); ?></label>
                <?php submit_button( __( 'Save Switches', 'yatoon-booking-system' ) ); ?>
            </form>

            <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:16px;">
                <?php self::render_coupon_box( $coupons ); ?>
                <?php self::render_package_box( $packages ); ?>
                <?php self::render_membership_box( $memberships ); ?>
                <?php self::render_customer_package_box( $packages, $customer_packages ); ?>
                <?php self::render_customer_membership_box( $memberships, $customer_memberships ); ?>
            </div>
        </div>
        <?php
    }

    private static function form_open( $type ) {
        echo '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '" style="background:#fff;border:1px solid #dcdcde;padding:16px;">';
        wp_nonce_field( 'yatoon_growth_save' );
        echo '<input type="hidden" name="action" value="yatoon_save_growth_module"><input type="hidden" name="module_type" value="' . esc_attr( $type ) . '">';
        echo '<input type="hidden" name="yatoon_enable_coupons" value="' . esc_attr( self::is_enabled( 'coupons' ) ? '1' : '0' ) . '">';
        echo '<input type="hidden" name="yatoon_enable_packages" value="' . esc_attr( self::is_enabled( 'packages' ) ? '1' : '0' ) . '">';
        echo '<input type="hidden" name="yatoon_enable_memberships" value="' . esc_attr( self::is_enabled( 'memberships' ) ? '1' : '0' ) . '">';
    }

    private static function render_coupon_box( $rows ) {
        self::form_open( 'coupon' ); ?>
        <h2><?php esc_html_e( 'Coupons', 'yatoon-booking-system' ); ?></h2>
        <p><input class="regular-text" name="coupon_code" placeholder="WELCOME10"></p>
        <p><input class="regular-text" name="coupon_description" placeholder="<?php esc_attr_e( 'Description', 'yatoon-booking-system' ); ?>"></p>
        <p><select name="coupon_discount_type"><option value="percent">Percent</option><option value="fixed">Fixed amount</option></select> <input type="number" step="0.01" name="coupon_amount" placeholder="10"></p>
        <p><input type="number" name="coupon_usage_limit" placeholder="<?php esc_attr_e( 'Usage limit, 0 unlimited', 'yatoon-booking-system' ); ?>"></p>
        <p><input type="date" name="coupon_starts_at"> <input type="date" name="coupon_expires_at"></p>
        <p><label><input type="checkbox" name="coupon_is_active" value="1" checked> <?php esc_html_e( 'Active', 'yatoon-booking-system' ); ?></label></p>
        <?php submit_button( __( 'Add / Update Coupon', 'yatoon-booking-system' ) ); ?>
        <ul><?php foreach ( (array) $rows as $r ) echo '<li><strong>' . esc_html( $r->code ) . '</strong> - ' . esc_html( $r->discount_type ) . ' ' . esc_html( $r->amount ) . '</li>'; ?></ul>
        </form><?php
    }

    private static function render_package_box( $rows ) {
        self::form_open( 'package' ); ?>
        <h2><?php esc_html_e( 'Packages', 'yatoon-booking-system' ); ?></h2>
        <p><input class="regular-text" name="package_name" placeholder="<?php esc_attr_e( 'Package name', 'yatoon-booking-system' ); ?>"></p>
        <p><textarea class="large-text" name="package_description" placeholder="<?php esc_attr_e( 'Description', 'yatoon-booking-system' ); ?>"></textarea></p>
        <p><input type="number" name="package_visits" min="1" placeholder="<?php esc_attr_e( 'Included visits', 'yatoon-booking-system' ); ?>"> <input type="number" step="0.01" name="package_price" placeholder="<?php esc_attr_e( 'Price', 'yatoon-booking-system' ); ?>"></p>
        <p><input type="number" name="package_validity_days" min="1" value="90"> <?php esc_html_e( 'valid days', 'yatoon-booking-system' ); ?></p>
        <p><label><input type="checkbox" name="package_is_active" value="1" checked> <?php esc_html_e( 'Active', 'yatoon-booking-system' ); ?></label></p>
        <?php submit_button( __( 'Add Package', 'yatoon-booking-system' ) ); ?>
        <ul><?php foreach ( (array) $rows as $r ) echo '<li><strong>' . esc_html( $r->name ) . '</strong> - ' . esc_html( $r->included_visits ) . ' visits / $' . esc_html( $r->price ) . '</li>'; ?></ul>
        </form><?php
    }

    private static function render_membership_box( $rows ) {
        self::form_open( 'membership' ); ?>
        <h2><?php esc_html_e( 'Memberships', 'yatoon-booking-system' ); ?></h2>
        <p><input class="regular-text" name="membership_name" placeholder="<?php esc_attr_e( 'Membership name', 'yatoon-booking-system' ); ?>"></p>
        <p><textarea class="large-text" name="membership_description" placeholder="<?php esc_attr_e( 'Description', 'yatoon-booking-system' ); ?>"></textarea></p>
        <p><select name="membership_period"><option value="monthly">Monthly</option><option value="yearly">Yearly</option></select> <input type="number" step="0.01" name="membership_price" placeholder="<?php esc_attr_e( 'Price', 'yatoon-booking-system' ); ?>"></p>
        <p><input type="number" step="0.01" name="membership_discount" placeholder="<?php esc_attr_e( 'Booking discount %', 'yatoon-booking-system' ); ?>"></p>
        <p><label><input type="checkbox" name="membership_is_active" value="1" checked> <?php esc_html_e( 'Active', 'yatoon-booking-system' ); ?></label></p>
        <?php submit_button( __( 'Add Membership', 'yatoon-booking-system' ) ); ?>
        <ul><?php foreach ( (array) $rows as $r ) echo '<li><strong>' . esc_html( $r->name ) . '</strong> - $' . esc_html( $r->price ) . ' / ' . esc_html( $r->billing_period ) . '</li>'; ?></ul>
        </form><?php
    }

    private static function render_customer_fields() {
        ?>
        <p><input class="regular-text" name="customer_name" placeholder="<?php esc_attr_e( 'Customer name', 'yatoon-booking-system' ); ?>"></p>
        <p><input class="regular-text" type="email" name="customer_email" placeholder="<?php esc_attr_e( 'Customer email', 'yatoon-booking-system' ); ?>"></p>
        <p><input class="regular-text" name="customer_phone" placeholder="<?php esc_attr_e( 'Customer phone', 'yatoon-booking-system' ); ?>"></p>
        <p><textarea class="large-text" name="customer_notes" placeholder="<?php esc_attr_e( 'Internal notes', 'yatoon-booking-system' ); ?>"></textarea></p>
        <?php
    }

    private static function render_customer_package_box( $packages, $rows ) {
        self::form_open( 'grant_package' ); ?>
        <h2><?php esc_html_e( 'Customer Packages', 'yatoon-booking-system' ); ?></h2>
        <?php self::render_customer_fields(); ?>
        <p><select name="customer_package_id">
            <?php foreach ( (array) $packages as $p ) : ?>
                <option value="<?php echo esc_attr( $p->id ); ?>"><?php echo esc_html( $p->name ); ?></option>
            <?php endforeach; ?>
        </select></p>
        <p><input type="number" name="customer_package_visits" min="1" placeholder="<?php esc_attr_e( 'Visits', 'yatoon-booking-system' ); ?>"> <input type="number" step="0.01" name="customer_package_paid_amount" placeholder="<?php esc_attr_e( 'Paid amount', 'yatoon-booking-system' ); ?>"></p>
        <p><input type="date" name="customer_package_starts_at"> <input type="date" name="customer_package_expires_at"></p>
        <?php submit_button( __( 'Assign Package', 'yatoon-booking-system' ) ); ?>
        <ul><?php foreach ( (array) $rows as $r ) echo '<li><strong>' . esc_html( $r->customer_name ?: $r->customer_email ?: $r->customer_phone ) . '</strong> - ' . esc_html( $r->package_name ) . ' (' . esc_html( $r->remaining_visits ) . '/' . esc_html( $r->purchased_visits ) . ')</li>'; ?></ul>
        </form><?php
    }

    private static function render_customer_membership_box( $memberships, $rows ) {
        self::form_open( 'grant_membership' ); ?>
        <h2><?php esc_html_e( 'Customer Memberships', 'yatoon-booking-system' ); ?></h2>
        <?php self::render_customer_fields(); ?>
        <p><select name="customer_membership_id">
            <?php foreach ( (array) $memberships as $m ) : ?>
                <option value="<?php echo esc_attr( $m->id ); ?>"><?php echo esc_html( $m->name ); ?></option>
            <?php endforeach; ?>
        </select></p>
        <p><input type="number" step="0.01" name="customer_membership_paid_amount" placeholder="<?php esc_attr_e( 'Paid amount', 'yatoon-booking-system' ); ?>"></p>
        <p><input type="date" name="customer_membership_starts_at"> <input type="date" name="customer_membership_expires_at"></p>
        <?php submit_button( __( 'Assign Membership', 'yatoon-booking-system' ) ); ?>
        <ul><?php foreach ( (array) $rows as $r ) echo '<li><strong>' . esc_html( $r->customer_name ?: $r->customer_email ?: $r->customer_phone ) . '</strong> - ' . esc_html( $r->membership_name ) . ' (' . esc_html( $r->status ) . ')</li>'; ?></ul>
        </form><?php
    }

    /* ---------------------------------------------------------------------
       Reports
       --------------------------------------------------------------------- */

    private static function render_reports_section() {
        global $wpdb;
        $today  = current_time( 'Y-m-d' );
        $month0 = current_time( 'Y-m-01' );

        // KPI: package revenue (active+expired+refunded all count toward sold).
        $pkg_revenue_total = floatval( $wpdb->get_var(
            "SELECT COALESCE(SUM(paid_amount),0) FROM {$wpdb->prefix}yatoon_customer_packages WHERE status IN ('active','expired')"
        ) );
        $pkg_revenue_month = floatval( $wpdb->get_var( $wpdb->prepare(
            "SELECT COALESCE(SUM(paid_amount),0) FROM {$wpdb->prefix}yatoon_customer_packages WHERE status IN ('active','expired') AND created_at >= %s",
            $month0 . ' 00:00:00'
        ) ) );
        $pkg_visits_sold = intval( $wpdb->get_var( "SELECT COALESCE(SUM(purchased_visits),0) FROM {$wpdb->prefix}yatoon_customer_packages WHERE status IN ('active','expired')" ) );
        $pkg_visits_left = intval( $wpdb->get_var( "SELECT COALESCE(SUM(remaining_visits),0) FROM {$wpdb->prefix}yatoon_customer_packages WHERE status = 'active'" ) );

        // KPI: membership active count + monthly recurring revenue.
        $mbr_active_count = intval( $wpdb->get_var( $wpdb->prepare(
            "SELECT COUNT(*) FROM {$wpdb->prefix}yatoon_customer_memberships WHERE status = 'active' AND ( expires_at IS NULL OR expires_at >= %s )",
            $today
        ) ) );
        $mbr_mrr_rows = $wpdb->get_results( $wpdb->prepare(
            "SELECT cm.paid_amount, m.billing_period
             FROM {$wpdb->prefix}yatoon_customer_memberships cm
             LEFT JOIN {$wpdb->prefix}yatoon_memberships m ON cm.membership_id = m.id
             WHERE cm.status = 'active' AND ( cm.expires_at IS NULL OR cm.expires_at >= %s )",
            $today
        ) );
        $mrr = 0.0;
        foreach ( (array) $mbr_mrr_rows as $r ) {
            $amt = floatval( $r->paid_amount );
            $mrr += ( $r->billing_period === 'yearly' ) ? ( $amt / 12.0 ) : $amt;
        }
        $mbr_revenue_month = floatval( $wpdb->get_var( $wpdb->prepare(
            "SELECT COALESCE(SUM(paid_amount),0) FROM {$wpdb->prefix}yatoon_customer_memberships WHERE status IN ('active','expired') AND created_at >= %s",
            $month0 . ' 00:00:00'
        ) ) );

        // KPI: redemptions and discount given.
        $red_total_count = intval( $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}yatoon_growth_redemptions" ) );
        $red_month_count = intval( $wpdb->get_var( $wpdb->prepare(
            "SELECT COUNT(*) FROM {$wpdb->prefix}yatoon_growth_redemptions WHERE created_at >= %s",
            $month0 . ' 00:00:00'
        ) ) );
        $red_discount_total = floatval( $wpdb->get_var( "SELECT COALESCE(SUM(amount),0) FROM {$wpdb->prefix}yatoon_growth_redemptions" ) );

        // KPI: coupons used count.
        $coupon_uses = intval( $wpdb->get_var( "SELECT COALESCE(SUM(used_count),0) FROM {$wpdb->prefix}yatoon_coupons" ) );

        // Lists.
        $top_coupons = $wpdb->get_results(
            "SELECT code, used_count, discount_type, amount FROM {$wpdb->prefix}yatoon_coupons
             WHERE used_count > 0 ORDER BY used_count DESC, id DESC LIMIT 5"
        );
        $expiring_pkgs = $wpdb->get_results( $wpdb->prepare(
            "SELECT cp.customer_name, cp.customer_email, cp.remaining_visits, cp.expires_at, p.name AS package_name
             FROM {$wpdb->prefix}yatoon_customer_packages cp
             LEFT JOIN {$wpdb->prefix}yatoon_packages p ON cp.package_id = p.id
             WHERE cp.status = 'active' AND cp.expires_at IS NOT NULL
               AND cp.expires_at BETWEEN %s AND %s
             ORDER BY cp.expires_at ASC LIMIT 8",
            $today, gmdate( 'Y-m-d', strtotime( $today . ' +14 days' ) )
        ) );
        $recent_redemptions = $wpdb->get_results(
            "SELECT r.module_type, r.amount, r.visits_used, r.created_at, b.customer_name, b.customer_email
             FROM {$wpdb->prefix}yatoon_growth_redemptions r
             LEFT JOIN {$wpdb->prefix}yatoon_bookings b ON r.booking_id = b.id
             ORDER BY r.id DESC LIMIT 10"
        );
        ?>
        <div class="yatoon-growth-reports" style="margin:16px 0 24px;">
            <h2 style="margin:0 0 8px;"><?php esc_html_e( 'Reports', 'yatoon-booking-system' ); ?></h2>
            <p class="description" style="margin:0 0 12px;"><?php esc_html_e( 'Snapshot of revenue, balances, and redemption activity. Updated in real-time.', 'yatoon-booking-system' ); ?></p>
            <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;">
                <?php
                /* translators: %s: package revenue this month. */
                self::render_kpi( __( 'Package revenue (all time)', 'yatoon-booking-system' ), '$' . number_format( $pkg_revenue_total, 2 ), sprintf( __( 'This month: $%s', 'yatoon-booking-system' ), number_format( $pkg_revenue_month, 2 ) ) );
                self::render_kpi( __( 'Visits sold / remaining', 'yatoon-booking-system' ), $pkg_visits_sold . ' / ' . $pkg_visits_left, __( 'Active packages only', 'yatoon-booking-system' ) );
                /* translators: %s: monthly recurring revenue. */
                self::render_kpi( __( 'Active memberships', 'yatoon-booking-system' ), (string) $mbr_active_count, sprintf( __( 'MRR: $%s', 'yatoon-booking-system' ), number_format( $mrr, 2 ) ) );
                self::render_kpi( __( 'Membership revenue (this month)', 'yatoon-booking-system' ), '$' . number_format( $mbr_revenue_month, 2 ), '' );
                /* translators: %d: total redemption count. */
                self::render_kpi( __( 'Redemptions (this month)', 'yatoon-booking-system' ), (string) $red_month_count, sprintf( __( 'All time: %d', 'yatoon-booking-system' ), $red_total_count ) );
                /* translators: %d: number of coupon uses. */
                self::render_kpi( __( 'Discount value redeemed', 'yatoon-booking-system' ), '$' . number_format( $red_discount_total, 2 ), sprintf( __( 'Coupon uses: %d', 'yatoon-booking-system' ), $coupon_uses ) );
                ?>
            </div>

            <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:16px;margin-top:16px;">
                <div style="background:#fff;border:1px solid #dcdcde;padding:14px;">
                    <h3 style="margin-top:0;"><?php esc_html_e( 'Top coupons', 'yatoon-booking-system' ); ?></h3>
                    <?php if ( empty( $top_coupons ) ) : ?>
                        <p class="description"><?php esc_html_e( 'No coupons have been used yet.', 'yatoon-booking-system' ); ?></p>
                    <?php else : ?>
                        <table class="widefat striped" style="margin:0;"><thead><tr>
                            <th><?php esc_html_e( 'Code', 'yatoon-booking-system' ); ?></th>
                            <th><?php esc_html_e( 'Type', 'yatoon-booking-system' ); ?></th>
                            <th><?php esc_html_e( 'Amount', 'yatoon-booking-system' ); ?></th>
                            <th><?php esc_html_e( 'Uses', 'yatoon-booking-system' ); ?></th>
                        </tr></thead><tbody>
                        <?php foreach ( $top_coupons as $c ) : ?>
                            <tr>
                                <td><code><?php echo esc_html( $c->code ); ?></code></td>
                                <td><?php echo esc_html( $c->discount_type ); ?></td>
                                <td><?php echo $c->discount_type === 'percent' ? esc_html( $c->amount ) . '%' : '$' . esc_html( number_format( floatval( $c->amount ), 2 ) ); ?></td>
                                <td><?php echo intval( $c->used_count ); ?></td>
                            </tr>
                        <?php endforeach; ?>
                        </tbody></table>
                    <?php endif; ?>
                </div>

                <div style="background:#fff;border:1px solid #dcdcde;padding:14px;">
                    <h3 style="margin-top:0;"><?php esc_html_e( 'Packages expiring in 14 days', 'yatoon-booking-system' ); ?></h3>
                    <?php if ( empty( $expiring_pkgs ) ) : ?>
                        <p class="description"><?php esc_html_e( 'No packages are expiring soon.', 'yatoon-booking-system' ); ?></p>
                    <?php else : ?>
                        <table class="widefat striped" style="margin:0;"><thead><tr>
                            <th><?php esc_html_e( 'Customer', 'yatoon-booking-system' ); ?></th>
                            <th><?php esc_html_e( 'Package', 'yatoon-booking-system' ); ?></th>
                            <th><?php esc_html_e( 'Visits left', 'yatoon-booking-system' ); ?></th>
                            <th><?php esc_html_e( 'Expires', 'yatoon-booking-system' ); ?></th>
                        </tr></thead><tbody>
                        <?php foreach ( $expiring_pkgs as $r ) : ?>
                            <tr>
                                <td><?php echo esc_html( $r->customer_name ?: $r->customer_email ); ?></td>
                                <td><?php echo esc_html( $r->package_name ); ?></td>
                                <td><?php echo intval( $r->remaining_visits ); ?></td>
                                <td><?php echo esc_html( $r->expires_at ); ?></td>
                            </tr>
                        <?php endforeach; ?>
                        </tbody></table>
                    <?php endif; ?>
                </div>

                <div style="background:#fff;border:1px solid #dcdcde;padding:14px;">
                    <h3 style="margin-top:0;"><?php esc_html_e( 'Recent redemptions', 'yatoon-booking-system' ); ?></h3>
                    <?php if ( empty( $recent_redemptions ) ) : ?>
                        <p class="description"><?php esc_html_e( 'No redemptions yet.', 'yatoon-booking-system' ); ?></p>
                    <?php else : ?>
                        <table class="widefat striped" style="margin:0;"><thead><tr>
                            <th><?php esc_html_e( 'Date', 'yatoon-booking-system' ); ?></th>
                            <th><?php esc_html_e( 'Type', 'yatoon-booking-system' ); ?></th>
                            <th><?php esc_html_e( 'Customer', 'yatoon-booking-system' ); ?></th>
                            <th><?php esc_html_e( 'Discount', 'yatoon-booking-system' ); ?></th>
                        </tr></thead><tbody>
                        <?php foreach ( $recent_redemptions as $r ) : ?>
                            <tr>
                                <td><?php echo esc_html( mysql2date( get_option( 'date_format' ) . ' g:ia', $r->created_at ) ); ?></td>
                                <td><?php echo esc_html( $r->module_type ); ?><?php echo intval( $r->visits_used ) ? ' (' . intval( $r->visits_used ) . 'v)' : ''; ?></td>
                                <td><?php echo esc_html( $r->customer_name ?: $r->customer_email ?: '-' ); ?></td>
                                <td>$<?php echo esc_html( number_format( floatval( $r->amount ), 2 ) ); ?></td>
                            </tr>
                        <?php endforeach; ?>
                        </tbody></table>
                    <?php endif; ?>
                </div>
            </div>
        </div>
        <?php
    }

    private static function render_kpi( $label, $value, $sub = '' ) {
        echo '<div style="background:#fff;border:1px solid #dcdcde;padding:14px;border-radius:6px;">';
        echo '<div style="font-size:0.85em;color:#646970;text-transform:uppercase;letter-spacing:0.04em;">' . esc_html( $label ) . '</div>';
        echo '<div style="font-size:1.6em;font-weight:600;margin-top:4px;">' . esc_html( $value ) . '</div>';
        if ( $sub !== '' ) {
            echo '<div style="font-size:0.85em;color:#646970;margin-top:4px;">' . esc_html( $sub ) . '</div>';
        }
        echo '</div>';
    }

    /* ---------------------------------------------------------------------
       Pending storefront purchases
       --------------------------------------------------------------------- */

    private static function render_pending_section( $packages, $memberships ) {
        ?>
        <div style="background:#fff;border:1px solid #dcdcde;padding:14px;margin:16px 0;">
            <h2 style="margin-top:0;"><?php esc_html_e( 'Pending storefront purchases', 'yatoon-booking-system' ); ?></h2>
            <p class="description" style="margin-top:0;"><?php esc_html_e( 'Customers requested these via the public storefront. Stripe-paid rows auto-activate; click "Activate" for manual / pay-in-store rows once you have collected payment.', 'yatoon-booking-system' ); ?></p>
            <?php
            $rows = array();
            foreach ( (array) $packages    as $r ) { $r->_type = 'package';    $r->_label = $r->package_name;    $rows[] = $r; }
            foreach ( (array) $memberships as $r ) { $r->_type = 'membership'; $r->_label = $r->membership_name; $rows[] = $r; }
            ?>
            <table class="widefat striped"><thead><tr>
                <th><?php esc_html_e( 'Type', 'yatoon-booking-system' ); ?></th>
                <th><?php esc_html_e( 'Item', 'yatoon-booking-system' ); ?></th>
                <th><?php esc_html_e( 'Customer', 'yatoon-booking-system' ); ?></th>
                <th><?php esc_html_e( 'Phone', 'yatoon-booking-system' ); ?></th>
                <th><?php esc_html_e( 'Amount', 'yatoon-booking-system' ); ?></th>
                <th><?php esc_html_e( 'Stripe', 'yatoon-booking-system' ); ?></th>
                <th><?php esc_html_e( 'Action', 'yatoon-booking-system' ); ?></th>
            </tr></thead><tbody>
            <?php foreach ( $rows as $r ) : ?>
                <tr>
                    <td><?php echo esc_html( $r->_type ); ?></td>
                    <td><?php echo esc_html( $r->_label ); ?></td>
                    <td><?php echo esc_html( $r->customer_name ); ?><br><span style="color:#646970;font-size:0.9em;"><?php echo esc_html( $r->customer_email ); ?></span></td>
                    <td><?php echo esc_html( $r->customer_phone ); ?></td>
                    <td>$<?php echo esc_html( number_format( floatval( $r->paid_amount ), 2 ) ); ?></td>
                    <td><?php echo $r->stripe_intent_id ? '<code>' . esc_html( substr( $r->stripe_intent_id, 0, 14 ) ) . '...</code>' : '-'; ?></td>
                    <td>
                        <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" style="display:inline;">
                            <?php wp_nonce_field( 'yatoon_growth_activate' ); ?>
                            <input type="hidden" name="action" value="yatoon_growth_activate_purchase">
                            <input type="hidden" name="purchase_type" value="<?php echo esc_attr( $r->_type ); ?>">
                            <input type="hidden" name="purchase_id" value="<?php echo intval( $r->id ); ?>">
                            <button type="submit" class="button button-primary"><?php esc_html_e( 'Activate', 'yatoon-booking-system' ); ?></button>
                        </form>
                    </td>
                </tr>
            <?php endforeach; ?>
            </tbody></table>
        </div>
        <?php
    }

    public static function handle_activate_purchase() {
        if ( ! current_user_can( 'manage_options' ) ) wp_die( 'Permission denied' );
        check_admin_referer( 'yatoon_growth_activate' );
        global $wpdb;
        $type = isset( $_POST['purchase_type'] ) ? sanitize_key( wp_unslash( $_POST['purchase_type'] ) ) : '';
        $id   = isset( $_POST['purchase_id'] )   ? absint( $_POST['purchase_id'] )                      : 0;
        $tbl  = '';
        if ( $type === 'package' )    $tbl = $wpdb->prefix . 'yatoon_customer_packages';
        if ( $type === 'membership' ) $tbl = $wpdb->prefix . 'yatoon_customer_memberships';
        if ( $tbl && $id ) {
            $wpdb->update( $tbl,
                array( 'status' => 'active', 'updated_at' => current_time( 'mysql' ) ),
                array( 'id' => $id, 'status' => 'pending' ),
                array( '%s', '%s' ),
                array( '%d', '%s' )
            );
        }
        wp_safe_redirect( admin_url( 'admin.php?page=yatoon-growth-modules&activated=1' ) );
        exit;
    }
}

YATOON_Growth_Modules::init();
