<?php
if (!defined('ABSPATH')) exit;

/**
 * Ensure vendor ratings table exists (idempotent).
 */
function stakmv_ensure_vendor_ratings_table() {
    global $wpdb;

    $table = STAKMV_VENDOR_RATINGS_TABLE;
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- One-time table existence check during init.
    $exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
    if ($exists === $table) {
        return;
    }

    require_once ABSPATH . 'wp-admin/includes/upgrade.php';
    $charset = $wpdb->get_charset_collate();

    $sql = "CREATE TABLE {$table} (
        id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        order_id BIGINT UNSIGNED NOT NULL,
        auction_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
        vendor_id BIGINT UNSIGNED NOT NULL,
        buyer_id BIGINT UNSIGNED NOT NULL,
        rating TINYINT UNSIGNED NOT NULL,
        review TEXT NULL,
        created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at DATETIME NULL,
        PRIMARY KEY (id),
        UNIQUE KEY uniq_order_vendor_buyer (order_id, vendor_id, buyer_id),
        KEY vendor_id (vendor_id),
        KEY buyer_id (buyer_id),
        KEY order_id (order_id)
    ) {$charset};";

    dbDelta($sql);
}
add_action('init', 'stakmv_ensure_vendor_ratings_table', 2);

/**
 * Get unique vendor IDs in an order that the buyer can rate.
 *
 * @param WC_Order|int $order
 * @param int          $buyer_id
 * @return int[]
 */
function stakmv_get_rateable_vendor_ids_from_order($order, $buyer_id) {
    $buyer_id = (int) $buyer_id;
    if ($buyer_id <= 0) {
        return array();
    }

    if (!($order instanceof WC_Order)) {
        $order = wc_get_order((int) $order);
    }
    if (!($order instanceof WC_Order)) {
        return array();
    }

    if ((int) $order->get_user_id() !== $buyer_id) {
        return array();
    }

    if (!in_array((string) $order->get_status(), array('completed'), true)) {
        return array();
    }

    $vendor_ids = array();
    foreach ($order->get_items() as $item) {
        if (!($item instanceof WC_Order_Item_Product)) {
            continue;
        }

        $product = $item->get_product();
        if (!$product) {
            continue;
        }

        $product_id = (int) $product->get_id();
        if ($product_id <= 0) {
            continue;
        }

        $vendor_id = (int) get_post_field('post_author', $product_id);
        if ($vendor_id <= 0 || $vendor_id === $buyer_id) {
            continue;
        }

        $vendor_ids[$vendor_id] = $vendor_id;
    }

    return array_values($vendor_ids);
}

/**
 * Get existing rating row for order/vendor/buyer.
 *
 * @return array|null
 */
function stakmv_get_order_vendor_rating($order_id, $vendor_id, $buyer_id) {
    global $wpdb;

    $ratings_table = $wpdb->prefix . 'stakmv_vendor_ratings';
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Read lookup is scoped; table name is plugin-controlled and escaped.
    $row = $wpdb->get_row(
        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Table name is escaped with esc_sql and controlled by plugin code.
        $wpdb->prepare(
            // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Dynamic table name uses current site prefix.
            "SELECT * FROM {$ratings_table} WHERE order_id = %d AND vendor_id = %d AND buyer_id = %d LIMIT 1",
            (int) $order_id,
            (int) $vendor_id,
            (int) $buyer_id
        ),
        ARRAY_A
    );

    return is_array($row) ? $row : null;
}

/**
 * Invalidate cached vendor rating summary.
 */
function stakmv_clear_vendor_rating_cache($vendor_id) {
    $vendor_id = (int) $vendor_id;
    if ($vendor_id <= 0) {
        return;
    }
    stakmv_delete_transient('vendor_rating_summary_' . $vendor_id);
}

/**
 * Invalidate cached vendor reputation summary.
 */
function stakmv_clear_vendor_reputation_cache($vendor_id) {
    $vendor_id = (int) $vendor_id;
    if ($vendor_id <= 0) {
        return;
    }
    stakmv_delete_transient('vendor_reputation_summary_' . $vendor_id);
}

/**
 * Get vendor IDs represented in a WooCommerce order.
 *
 * @param WC_Order|int $order
 * @return int[]
 */
function stakmv_get_vendor_ids_from_order($order) {
    if (!($order instanceof WC_Order)) {
        $order = wc_get_order((int) $order);
    }
    if (!($order instanceof WC_Order)) {
        return array();
    }

    $vendor_ids = array();
    foreach ($order->get_items() as $item) {
        if (!($item instanceof WC_Order_Item_Product)) {
            continue;
        }

        $product = $item->get_product();
        if (!$product) {
            continue;
        }

        $product_id = (int) $product->get_id();
        if ($product_id <= 0) {
            continue;
        }

        // For variations, use parent product author as seller.
        $author_product_id = $product->is_type('variation')
            ? (int) $product->get_parent_id()
            : $product_id;
        if ($author_product_id <= 0) {
            $author_product_id = $product_id;
        }

        $vendor_id = (int) get_post_field('post_author', $author_product_id);
        if ($vendor_id > 0) {
            $vendor_ids[$vendor_id] = $vendor_id;
        }
    }

    return array_values($vendor_ids);
}

/**
 * Get vendor rating summary.
 *
 * @return array{average:float,count:int,stars:array<int,int>}
 */
function stakmv_get_vendor_rating_summary($vendor_id) {
    global $wpdb;

    $vendor_id = (int) $vendor_id;
    if ($vendor_id <= 0) {
        return array(
            'average' => 0.0,
            'count'   => 0,
            'stars'   => array(1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0),
        );
    }

    $cache_key = 'stakmv_vendor_rating_summary_' . $vendor_id;
    $cached = get_transient($cache_key);
    if (is_array($cached)) {
        return $cached;
    }

    $ratings_table = $wpdb->prefix . 'stakmv_vendor_ratings';
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Aggregate query is scoped to one vendor; table name is plugin-controlled and escaped.
    $agg = $wpdb->get_row(
        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Table name is escaped with esc_sql and controlled by plugin code.
        $wpdb->prepare(
            // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Dynamic table name uses current site prefix.
            "SELECT AVG(rating) AS average_rating, COUNT(*) AS total_count FROM {$ratings_table} WHERE vendor_id = %d",
            $vendor_id
        ),
        ARRAY_A
    );

    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Aggregate query is scoped to one vendor; table name is plugin-controlled and escaped.
    $breakdown_rows = $wpdb->get_results(
        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Table name is escaped with esc_sql and controlled by plugin code.
        $wpdb->prepare(
            // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Dynamic table name uses current site prefix.
            "SELECT rating, COUNT(*) AS total FROM {$ratings_table} WHERE vendor_id = %d GROUP BY rating",
            $vendor_id
        ),
        ARRAY_A
    );

    $stars = array(1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0);
    if (is_array($breakdown_rows)) {
        foreach ($breakdown_rows as $row) {
            $star = isset($row['rating']) ? (int) $row['rating'] : 0;
            if ($star >= 1 && $star <= 5) {
                $stars[$star] = (int) ($row['total'] ?? 0);
            }
        }
    }

    $summary = array(
        'average' => isset($agg['average_rating']) ? round((float) $agg['average_rating'], 2) : 0.0,
        'count'   => isset($agg['total_count']) ? (int) $agg['total_count'] : 0,
        'stars'   => $stars,
    );

    set_transient($cache_key, $summary, 15 * MINUTE_IN_SECONDS);
    return $summary;
}

/**
 * Public-facing seller reputation summary.
 *
 * @return array{
 *   rating_average:float,
 *   rating_count:int,
 *   fulfillment_rate:float,
 *   cancellation_rate:float,
 *   dispute_rate:float,
 *   repeat_buyers_rate:float,
 *   avg_shipping_days:float,
 *   total_orders:int,
 *   completed_orders:int,
 *   cancelled_orders:int,
 *   disputed_orders:int,
 *   unique_buyers:int,
 *   repeat_buyers:int
 * }
 */
function stakmv_get_vendor_reputation_summary($vendor_id) {
    global $wpdb;

    $vendor_id = (int) $vendor_id;
    if ($vendor_id <= 0) {
        return array(
            'rating_average'      => 0.0,
            'rating_count'        => 0,
            'fulfillment_rate'    => 0.0,
            'cancellation_rate'   => 0.0,
            'dispute_rate'        => 0.0,
            'repeat_buyers_rate'  => 0.0,
            'avg_shipping_days'   => 0.0,
            'total_orders'        => 0,
            'completed_orders'    => 0,
            'cancelled_orders'    => 0,
            'disputed_orders'     => 0,
            'unique_buyers'       => 0,
            'repeat_buyers'       => 0,
        );
    }

    $cache_key = 'stakmv_vendor_reputation_summary_' . $vendor_id;
    $cached = get_transient($cache_key);
    if (is_array($cached)) {
        return $cached;
    }

    $lookup_table = $wpdb->prefix . 'wc_order_product_lookup';
    $posts_table  = $wpdb->posts;

    $order_ids_query = '';
    // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Core table names with prepared vendor values.
    $order_ids_query = $wpdb->prepare(
        'SELECT DISTINCT opl.order_id FROM ' . $lookup_table . ' opl INNER JOIN ' . $posts_table . ' p ON p.ID = opl.product_id LEFT JOIN ' . $posts_table . ' pp ON pp.ID = p.post_parent WHERE (p.post_type = \'product\' AND p.post_author = %d) OR (p.post_type = \'product_variation\' AND pp.post_author = %d)',
        $vendor_id,
        $vendor_id
    );
    // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- Vendor reputation query is read-only and cached.
    $order_ids = $wpdb->get_col($order_ids_query);

    $order_ids = array_values(array_unique(array_map('intval', is_array($order_ids) ? $order_ids : array())));
    if (empty($order_ids)) {
        $rating_summary = stakmv_get_vendor_rating_summary($vendor_id);
        $empty = array(
            'rating_average'      => (float) ($rating_summary['average'] ?? 0),
            'rating_count'        => (int) ($rating_summary['count'] ?? 0),
            'fulfillment_rate'    => 0.0,
            'cancellation_rate'   => 0.0,
            'dispute_rate'        => 0.0,
            'repeat_buyers_rate'  => 0.0,
            'avg_shipping_days'   => 0.0,
            'total_orders'        => 0,
            'completed_orders'    => 0,
            'cancelled_orders'    => 0,
            'disputed_orders'     => 0,
            'unique_buyers'       => 0,
            'repeat_buyers'       => 0,
        );
        set_transient($cache_key, $empty, 15 * MINUTE_IN_SECONDS);
        return $empty;
    }

    $total_orders      = 0;
    $completed_orders  = 0;
    $cancelled_orders  = 0;
    $shipping_days_sum = 0.0;
    $shipping_days_cnt = 0;
    $buyers_completed  = array();

    foreach ($order_ids as $order_id) {
        $order = wc_get_order($order_id);
        if (!($order instanceof WC_Order)) {
            continue;
        }

        // Safety: ensure this order still contains this vendor's item.
        if (!in_array($vendor_id, stakmv_get_vendor_ids_from_order($order), true)) {
            continue;
        }

        $status = (string) $order->get_status();
        if ($status === '') {
            continue;
        }

        $total_orders++;

        if ($status === 'completed') {
            $completed_orders++;

            $buyer_id = (int) $order->get_user_id();
            if ($buyer_id > 0) {
                if (!isset($buyers_completed[$buyer_id])) {
                    $buyers_completed[$buyer_id] = 0;
                }
                $buyers_completed[$buyer_id]++;
            }

            $completed_at = $order->get_date_completed();
            $start_at     = $order->get_date_paid();
            if (!$start_at) {
                $start_at = $order->get_date_created();
            }
            if ($completed_at && $start_at) {
                $delta = max(0, (int) $completed_at->getTimestamp() - (int) $start_at->getTimestamp());
                $shipping_days_sum += ($delta / DAY_IN_SECONDS);
                $shipping_days_cnt++;
            }
        } elseif (in_array($status, array('cancelled', 'failed', 'refunded'), true)) {
            $cancelled_orders++;
        }
    }

    $dispute_comments = get_comments(array(
        'type'      => 'stakmv_dispute',
        'post__in'  => $order_ids,
        'status'    => 'approve',
        'number'    => 0,
        'fields'    => 'ids',
    ));
    $disputed_orders = 0;
    if (is_array($dispute_comments) && !empty($dispute_comments)) {
        $dispute_order_ids = array();
        foreach ($dispute_comments as $comment_id) {
            $comment = get_comment((int) $comment_id);
            if ($comment && !empty($comment->comment_post_ID)) {
                $dispute_order_ids[(int) $comment->comment_post_ID] = true;
            }
        }
        $disputed_orders = count($dispute_order_ids);
    }

    $unique_buyers  = count($buyers_completed);
    $repeat_buyers  = 0;
    foreach ($buyers_completed as $order_count) {
        if ((int) $order_count >= 2) {
            $repeat_buyers++;
        }
    }

    $rating_summary = stakmv_get_vendor_rating_summary($vendor_id);
    $summary = array(
        'rating_average'      => (float) ($rating_summary['average'] ?? 0),
        'rating_count'        => (int) ($rating_summary['count'] ?? 0),
        'fulfillment_rate'    => $total_orders > 0 ? round((($completed_orders / $total_orders) * 100), 1) : 0.0,
        'cancellation_rate'   => $total_orders > 0 ? round((($cancelled_orders / $total_orders) * 100), 1) : 0.0,
        'dispute_rate'        => $total_orders > 0 ? round((($disputed_orders / $total_orders) * 100), 1) : 0.0,
        'repeat_buyers_rate'  => $unique_buyers > 0 ? round((($repeat_buyers / $unique_buyers) * 100), 1) : 0.0,
        'avg_shipping_days'   => $shipping_days_cnt > 0 ? round(($shipping_days_sum / $shipping_days_cnt), 1) : 0.0,
        'total_orders'        => (int) $total_orders,
        'completed_orders'    => (int) $completed_orders,
        'cancelled_orders'    => (int) $cancelled_orders,
        'disputed_orders'     => (int) $disputed_orders,
        'unique_buyers'       => (int) $unique_buyers,
        'repeat_buyers'       => (int) $repeat_buyers,
    );

    set_transient($cache_key, $summary, 15 * MINUTE_IN_SECONDS);
    return $summary;
}

/**
 * Clear reputation cache whenever an order status changes.
 */
function stakmv_invalidate_vendor_reputation_for_order($order_id) {
    $order = wc_get_order((int) $order_id);
    if (!($order instanceof WC_Order)) {
        return;
    }
    $vendor_ids = stakmv_get_vendor_ids_from_order($order);
    foreach ($vendor_ids as $vendor_id) {
        stakmv_clear_vendor_reputation_cache((int) $vendor_id);
    }
}
add_action('woocommerce_order_status_changed', 'stakmv_invalidate_vendor_reputation_for_order', 10, 1);

/**
 * Clear reputation cache when dispute comments are added.
 */
function stakmv_invalidate_vendor_reputation_for_dispute($comment_id, $comment_data) {
    $comment_type = '';
    $order_id = 0;

    if ($comment_data instanceof WP_Comment) {
        $comment_type = (string) $comment_data->comment_type;
        $order_id = (int) $comment_data->comment_post_ID;
    } elseif (is_array($comment_data)) {
        $comment_type = (string) ($comment_data['comment_type'] ?? '');
        $order_id = isset($comment_data['comment_post_ID']) ? (int) $comment_data['comment_post_ID'] : 0;
    }

    if ($comment_type !== 'stakmv_dispute') {
        return;
    }
    if ($order_id <= 0) {
        return;
    }

    $order = wc_get_order($order_id);
    if (!($order instanceof WC_Order)) {
        return;
    }

    $vendor_ids = stakmv_get_vendor_ids_from_order($order);
    foreach ($vendor_ids as $vendor_id) {
        stakmv_clear_vendor_reputation_cache((int) $vendor_id);
    }
}
add_action('wp_insert_comment', 'stakmv_invalidate_vendor_reputation_for_dispute', 10, 2);

/**
 * Handle rating create/update from buyer dashboard.
 */
function stakmv_handle_vendor_rating_submission() {
    $request_method = filter_input(INPUT_SERVER, 'REQUEST_METHOD', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
    // phpcs:ignore WordPress.Security.NonceVerification.Missing -- This only detects the form before the nonce is read and verified below.
    $submitted = filter_input(INPUT_POST, 'stakmv_submit_vendor_rating', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
    if ('POST' !== $request_method || !$submitted) {
        return;
    }

    if (!is_user_logged_in()) {
        return;
    }

    $redirect = wp_get_referer();
    if (!$redirect) {
        $redirect = home_url('/');
    }

    // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reading the nonce value in order to verify it immediately.
    $nonce = filter_input(INPUT_POST, 'stakmv_vendor_rating_nonce', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
    if (!$nonce || !wp_verify_nonce(sanitize_text_field((string) $nonce), 'stakmv_rate_vendor')) {
        wp_safe_redirect(
            add_query_arg(
                array(
                    'stakmv_rating_error'        => 'nonce',
                    'stakmv_rating_notice_nonce' => wp_create_nonce('stakmv_rating_notice'),
                ),
                $redirect
            )
        );
        exit;
    }

    $buyer_id = get_current_user_id();
    // phpcs:disable WordPress.Security.NonceVerification.Missing -- stakmv_rate_vendor nonce is verified above before reading rating payload.
    $order_id = absint(filter_input(INPUT_POST, 'order_id', FILTER_SANITIZE_NUMBER_INT));
    $vendor_id = absint(filter_input(INPUT_POST, 'vendor_id', FILTER_SANITIZE_NUMBER_INT));
    $rating = absint(filter_input(INPUT_POST, 'stakmv_vendor_rating', FILTER_SANITIZE_NUMBER_INT));
    $review_raw = filter_input(INPUT_POST, 'stakmv_vendor_review', FILTER_UNSAFE_RAW);
    // phpcs:enable WordPress.Security.NonceVerification.Missing
    $review = sanitize_textarea_field((string) $review_raw);

    if ($order_id <= 0 || $vendor_id <= 0 || $rating < 1 || $rating > 5) {
        wp_safe_redirect(
            add_query_arg(
                array(
                    'stakmv_rating_error'        => 'invalid',
                    'stakmv_rating_notice_nonce' => wp_create_nonce('stakmv_rating_notice'),
                ),
                $redirect
            )
        );
        exit;
    }

    $order = wc_get_order($order_id);
    $rateable_vendor_ids = stakmv_get_rateable_vendor_ids_from_order($order, $buyer_id);
    if (!in_array($vendor_id, $rateable_vendor_ids, true)) {
        wp_safe_redirect(
            add_query_arg(
                array(
                    'stakmv_rating_error'        => 'not_allowed',
                    'stakmv_rating_notice_nonce' => wp_create_nonce('stakmv_rating_notice'),
                ),
                $redirect
            )
        );
        exit;
    }

    global $wpdb;
    $ratings_table = $wpdb->prefix . 'stakmv_vendor_ratings';

    $existing = stakmv_get_order_vendor_rating($order_id, $vendor_id, $buyer_id);

    $auction_id = 0;
    if ($order instanceof WC_Order) {
        $raw_stakmv_auction_id = $order->get_meta('_stakmv_auction_id');
        $auction_id = $raw_stakmv_auction_id ? (int) $raw_stakmv_auction_id : 0;
    }

    if ($existing) {
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Intentional write for updating existing rating.
        $wpdb->update(
            $ratings_table,
            array(
                'rating'     => $rating,
                'review'     => $review,
                'updated_at' => current_time('mysql'),
            ),
            array('id' => (int) $existing['id']),
            array('%d', '%s', '%s'),
            array('%d')
        );
    } else {
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Intentional write for creating new rating.
        $wpdb->insert(
            $ratings_table,
            array(
                'order_id'   => $order_id,
                'auction_id' => $auction_id,
                'vendor_id'  => $vendor_id,
                'buyer_id'   => $buyer_id,
                'rating'     => $rating,
                'review'     => $review,
                'created_at' => current_time('mysql'),
            ),
            array('%d', '%d', '%d', '%d', '%d', '%s', '%s')
        );
    }

    stakmv_clear_vendor_rating_cache($vendor_id);
    stakmv_clear_vendor_reputation_cache($vendor_id);

    wp_safe_redirect(
        add_query_arg(
            array(
                'stakmv_rating_saved'        => 1,
                'stakmv_rating_notice_nonce' => wp_create_nonce('stakmv_rating_notice'),
            ),
            $redirect
        )
    );
    exit;
}
add_action('init', 'stakmv_handle_vendor_rating_submission', 20);

/**
 * Notice in My Purchases panel.
 */
function stakmv_render_vendor_rating_notice() {
    $saved = filter_input(INPUT_GET, 'stakmv_rating_saved', FILTER_SANITIZE_NUMBER_INT);
    $error = filter_input(INPUT_GET, 'stakmv_rating_error', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
    $notice_nonce = filter_input(INPUT_GET, 'stakmv_rating_notice_nonce', FILTER_SANITIZE_FULL_SPECIAL_CHARS);

    if (!$saved && !$error) {
        return;
    }

    if (!$notice_nonce || !wp_verify_nonce($notice_nonce, 'stakmv_rating_notice')) {
        return;
    }

    if ($saved) {
        echo '<div class="notice notice-success wcmv-my-10"><p>' . esc_html__('Your seller rating has been saved.', 'stak-multivendor') . '</p></div>';
        return;
    }

    $error = sanitize_key((string) $error);
    $message = esc_html__('Unable to save rating.', 'stak-multivendor');
    if ($error === 'nonce') {
        $message = esc_html__('Security check failed. Please try again.', 'stak-multivendor');
    } elseif ($error === 'not_allowed') {
        $message = esc_html__('You can only rate sellers for your completed purchases.', 'stak-multivendor');
    } elseif ($error === 'invalid') {
        $message = esc_html__('Please provide a valid star rating.', 'stak-multivendor');
    }

    echo '<div class="notice notice-error wcmv-my-10"><p>' . esc_html($message) . '</p></div>';
}
add_action('stakmv_vendor_dashboard_panel_completed_purchases', 'stakmv_render_vendor_rating_notice', 5);

/**
 * Render per-vendor rating form in purchases table.
 */
function stakmv_render_completed_purchase_rating_action($order) {
    if (!is_user_logged_in() || !($order instanceof WC_Order)) {
        return;
    }

    $buyer_id = get_current_user_id();
    $order_id = (int) $order->get_id();
    $vendor_ids = stakmv_get_rateable_vendor_ids_from_order($order, $buyer_id);

    if (empty($vendor_ids)) {
        return;
    }

    foreach ($vendor_ids as $vendor_id) {
        $vendor = get_userdata($vendor_id);
        if (!$vendor) {
            continue;
        }

        $existing = stakmv_get_order_vendor_rating($order_id, $vendor_id, $buyer_id);
        $selected_rating = $existing ? (int) $existing['rating'] : 5;
        $review_text = $existing ? (string) ($existing['review'] ?? '') : '';

        echo '<form method="post" class="wcmv-rating-form">';
        wp_nonce_field('stakmv_rate_vendor', 'stakmv_vendor_rating_nonce');
        echo '<input type="hidden" name="stakmv_submit_vendor_rating" value="1">';
        echo '<input type="hidden" name="order_id" value="' . esc_attr((string) $order_id) . '">';
        echo '<input type="hidden" name="vendor_id" value="' . esc_attr((string) $vendor_id) . '">';
        echo '<div class="wcmv-rating-form-title">' . esc_html__('Rate seller:', 'stak-multivendor') . ' ' . esc_html($vendor->display_name) . '</div>';

        echo '<select name="stakmv_vendor_rating" class="wcmv-rating-select">';
        for ($star = 5; $star >= 1; $star--) {
            echo '<option value="' . esc_attr((string) $star) . '"' . selected($selected_rating, $star, false) . '>' . esc_html(sprintf('%d ★', $star)) . '</option>';
        }
        echo '</select>';

        echo '<br><textarea name="stakmv_vendor_review" rows="2" maxlength="500" placeholder="' . esc_attr__('Optional review', 'stak-multivendor') . '" class="wcmv-rating-review">' . esc_textarea($review_text) . '</textarea>';

        echo '<br><button type="submit" class="button wcmv-rating-submit">' . esc_html($existing ? __('Update Rating', 'stak-multivendor') : __('Submit Rating', 'stak-multivendor')) . '</button>';
        echo '</form>';
    }
}
add_action('stakmv_vendor_dashboard_completed_purchase_row_actions', 'stakmv_render_completed_purchase_rating_action', 30, 1);
