<?php
/**
 * AJAX Handlers for Dashboard & Enhanced Features
 *
 * @package CustomSchemaSchemafy
 * @since 1.2.0
 */

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

/**
 * Sanitize a bulk schema-update payload coming from $_POST.
 *
 * Keys are normalized; text fields are run through sanitize_text_field while
 * schema_data is left intact (it is JSON validated downstream before storage).
 *
 * @param mixed $raw Raw (unslashed) updates array.
 * @return array Sanitized field => value map.
 */
function schemafy_sanitize_schema_updates($raw) {
    $clean = array();
    if (!is_array($raw)) {
        return $clean;
    }
    foreach ($raw as $key => $value) {
        $key = sanitize_key($key);
        if ($key === 'schema_data') {
            $clean[$key] = is_string($value) ? $value : '';
        } elseif (is_scalar($value)) {
            $clean[$key] = sanitize_text_field($value);
        }
    }
    return $clean;
}

/**
 * Get statistics for dashboard
 */
function schemafy_ajax_get_stats() {
    // Check permissions
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Unauthorized', 'schemafy'));
    }

    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'schemafy_nonce')) {
        wp_send_json_error(__('Invalid nonce', 'schemafy'));
    }

    global $schemafy_core_instance;
    $db = $schemafy_core_instance->get_database();

    // Get counts
    $counts = $db->get_schema_counts();
    $all_urls = $db->get_all_urls();

    // Count real published content — same whitelist as the auto-generator scanner:
    // post + page + product (WooCommerce) + custom types with show_in_nav_menus=true
    // that don't use known plugin-namespace prefixes. This excludes Elementor templates,
    // WP block-editor internals, page-builder UI primitives, etc.
    $scan_types = array( 'post', 'page' );
    if ( post_type_exists( 'product' ) ) {
        $scan_types[] = 'product';
    }
    $bad_prefixes  = array( 'elementor', 'e-', 'fl-', 'et_pb', 'vc_', 'wp_', 'acf', 'shop_', 'wc_', 'oembed', 'customize', 'nav_menu' );
    $custom_types  = get_post_types( array( 'public' => true, '_builtin' => false, 'show_in_nav_menus' => true ), 'names' );
    foreach ( $custom_types as $slug ) {
        $skip = false;
        foreach ( $bad_prefixes as $prefix ) {
            if ( strpos( $slug, $prefix ) === 0 ) { $skip = true; break; }
        }
        if ( ! $skip && ! in_array( $slug, $scan_types, true ) ) {
            $scan_types[] = $slug;
        }
    }

    $total_published_pages = 0;
    foreach ( $scan_types as $type ) {
        $type_counts = wp_count_posts( $type );
        if ( isset( $type_counts->publish ) ) {
            $total_published_pages += (int) $type_counts->publish;
        }
    }

    $stats = array(
        'total_schemas'          => $counts['total'],
        'active_schemas'         => $counts['active'],
        'inactive_schemas'       => $counts['inactive'],
        'total_urls'             => count( $all_urls ),
        'total_published_pages'  => $total_published_pages,
    );

    wp_send_json_success($stats);
}
add_action('wp_ajax_schemafy_get_stats', 'schemafy_ajax_get_stats');

/**
 * Get URL map with schemas (with pagination)
 */
function schemafy_ajax_get_url_map() {
    // Check permissions
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Unauthorized', 'schemafy'));
    }

    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'schemafy_nonce')) {
        wp_send_json_error(__('Invalid nonce', 'schemafy'));
    }

    global $wpdb, $schemafy_core_instance;
    $db = $schemafy_core_instance->get_database();
    $table_name = $db->get_table_name();

    // Get pagination parameters
    $page = isset($_POST['page']) ? intval($_POST['page']) : 1;
    $per_page = isset($_POST['per_page']) ? intval($_POST['per_page']) : 20;
    $search = isset($_POST['search']) ? sanitize_text_field(wp_unslash($_POST['search'])) : '';
    $offset = ($page - 1) * $per_page;

    // Get all schemas grouped by URL (with optional search filter)
    if (!empty($search)) {
        $like = '%' . $wpdb->esc_like($search) . '%';
        $all_urls = $wpdb->get_col($wpdb->prepare(
            "SELECT DISTINCT url FROM $table_name WHERE url LIKE %s OR schema_name LIKE %s OR schema_type LIKE %s ORDER BY url ASC",
            $like, $like, $like
        ));
    } else {
        $all_urls = $wpdb->get_col("SELECT DISTINCT url FROM $table_name ORDER BY url ASC"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- $table_name comes from $wpdb->prefix (trusted), identifiers cannot be placeholders, and this is a custom table with no object cache
    }
    $total_urls = count($all_urls);

    // Get paginated URLs
    $paginated_urls = array_slice($all_urls, $offset, $per_page);

    if (empty($paginated_urls)) {
        wp_send_json_success(array(
            'urls' => array(),
            'total' => $total_urls,
            'page' => $page,
            'per_page' => $per_page,
            'total_pages' => $total_urls > 0 ? ceil($total_urls / $per_page) : 0
        ));
        return;
    }

    // Get schemas for paginated URLs — spread array as individual args for wpdb->prepare
    $placeholders = implode( ', ', array_fill( 0, count( $paginated_urls ), '%s' ) );
    $query = $wpdb->prepare(
        "SELECT url, schema_type, is_active
         FROM $table_name
         WHERE url IN ($placeholders)
         ORDER BY url ASC",
        ...$paginated_urls
    );

    $results = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $query already prepared above

    if ( $wpdb->last_error ) {
        schemafy_log( '[SCHEMAFY URL Map] DB error on page ' . $page . ': ' . $wpdb->last_error );
    }

    // Group by URL
    $url_map = array();
    foreach ($results as $row) {
        if (!isset($url_map[$row->url])) {
            $url_map[$row->url] = array(
                'url' => $row->url,
                'count' => 0,
                'schemas' => array()
            );
        }

        $url_map[$row->url]['count']++;
        $url_map[$row->url]['schemas'][] = array(
            'type' => $row->schema_type,
            'is_active' => (bool) $row->is_active
        );
    }

    wp_send_json_success(array(
        'urls' => array_values($url_map),
        'total' => $total_urls,
        'page' => $page,
        'per_page' => $per_page,
        'total_pages' => ceil($total_urls / $per_page)
    ));
}
add_action('wp_ajax_schemafy_get_url_map', 'schemafy_ajax_get_url_map');

/**
 * Get schema types distribution
 */
function schemafy_ajax_get_types_distribution() {
    // Check permissions
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Unauthorized', 'schemafy'));
    }

    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'schemafy_nonce')) {
        wp_send_json_error(__('Invalid nonce', 'schemafy'));
    }

    global $wpdb, $schemafy_core_instance;
    $db = $schemafy_core_instance->get_database();
    $table_name = $db->get_table_name();

    // Get schema types count
    $results = $wpdb->get_results(
        "SELECT schema_type, COUNT(*) as count
         FROM $table_name
         WHERE is_active = 1
         GROUP BY schema_type
         ORDER BY count DESC"
    );

    $distribution = array();
    foreach ($results as $row) {
        $distribution[$row->schema_type] = (int) $row->count;
    }

    wp_send_json_success($distribution);
}
add_action('wp_ajax_schemafy_get_types_distribution', 'schemafy_ajax_get_types_distribution');

/**
 * Get schema field templates for builder
 */
function schemafy_ajax_get_schema_fields() {
    // Check permissions
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Unauthorized', 'schemafy'));
    }

    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'schemafy_nonce')) {
        wp_send_json_error(__('Invalid nonce', 'schemafy'));
    }

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

    // Load translated field config through the shared field generator.
    if (class_exists('SCHEMAFY_Field_Generator')) {
        $field_generator = new SCHEMAFY_Field_Generator();
        $config = $field_generator->get_field_config($type);

        if ($config && isset($config['fields'])) {
            wp_send_json_success(array(
                'type' => $type,
                'config' => $config,
                'template_available' => true
            ));
        }
    }

    // Fallback: return basic response
    wp_send_json_success(array(
        'type' => $type,
        'template_available' => false
    ));
}
add_action('wp_ajax_schemafy_get_schema_fields', 'schemafy_ajax_get_schema_fields');

/**
 * Get all schemas for bulk manager
 */
function schemafy_ajax_get_all_schemas() {
    // Check permissions
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Unauthorized', 'schemafy'));
    }

    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'schemafy_nonce')) {
        wp_send_json_error(__('Invalid nonce', 'schemafy'));
    }

    global $schemafy_core_instance;
    $db = $schemafy_core_instance->get_database();

    // Get pagination parameters
    $page = isset($_POST['page']) ? intval($_POST['page']) : 1;
    $per_page = isset($_POST['per_page']) ? intval($_POST['per_page']) : 100;

    // Get all schemas with pagination
    $result = $db->get_schemas(array(
        'page' => $page,
        'per_page' => $per_page,
        'orderby' => 'updated_at',
        'order' => 'DESC'
    ));

    // Ensure we always return an array - get_schemas() returns 'items' not 'schemas'
    $schemas = isset($result['items']) && is_array($result['items']) ? $result['items'] : array();
    $total = isset($result['total']) ? intval($result['total']) : 0;

    // Add creator name to each schema (handle missing created_by for old schemas)
    foreach ($schemas as &$schema) {
        $created_by = isset($schema['created_by']) ? intval($schema['created_by']) : 0;
        if ($created_by > 0) {
            $user = get_userdata($created_by);
            $schema['created_by_name'] = $user ? $user->display_name : '';
        } else {
            $schema['created_by_name'] = '';
        }
        // Ensure created_at exists
        if (!isset($schema['created_at'])) {
            $schema['created_at'] = '';
        }
        // Ensure updated_at exists
        if (!isset($schema['updated_at'])) {
            $schema['updated_at'] = '';
        }
    }

    wp_send_json_success(array(
        'schemas' => $schemas,
        'total' => $total,
        'page' => $page,
        'per_page' => $per_page,
        'total_pages' => ceil($total / $per_page)
    ));
}
add_action('wp_ajax_schemafy_get_all_schemas', 'schemafy_ajax_get_all_schemas');

/**
 * Toggle schema status (active/inactive)
 */
function schemafy_ajax_toggle_schema() {
    // Check permissions
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Unauthorized', 'schemafy'));
    }

    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'schemafy_nonce')) {
        wp_send_json_error(__('Invalid nonce', 'schemafy'));
    }

    // Get schema ID
    $id = isset($_POST['schema_id']) ? intval($_POST['schema_id']) : (isset($_POST['id']) ? intval($_POST['id']) : 0);
    if (!$id) {
        wp_send_json_error(__('Invalid ID', 'schemafy'));
    }

    // Get optional status parameter (nonce verified above)
    $status = isset($_POST['status'])
        ? ( '1' === sanitize_text_field( wp_unslash( $_POST['status'] ) ) )
        : null;

    global $schemafy_core_instance;
    if (!$schemafy_core_instance || !method_exists($schemafy_core_instance, 'get_database')) {
        wp_send_json_error(__('Database not available', 'schemafy'));
    }

    $db = $schemafy_core_instance->get_database();

    // Call toggle_schema with optional status
    $result = $db->toggle_schema($id, $status);

    if ($result !== false) {
        wp_send_json_success(array(
            'message' => __('Schema status toggled', 'schemafy'),
            'new_status' => $result
        ));
    } else {
        wp_send_json_error(__('Failed to toggle schema status', 'schemafy'));
    }
}
add_action('wp_ajax_schemafy_toggle_schema', 'schemafy_ajax_toggle_schema');

/**
 * Delete schema
 */
function schemafy_ajax_delete_schema() {
    // Check permissions
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Unauthorized', 'schemafy'));
    }

    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'schemafy_nonce')) {
        wp_send_json_error(__('Invalid nonce', 'schemafy'));
    }

    // Get schema ID
    $id = isset($_POST['schema_id']) ? intval($_POST['schema_id']) : (isset($_POST['id']) ? intval($_POST['id']) : 0);
    if (!$id) {
        wp_send_json_error(__('Invalid ID', 'schemafy'));
    }

    global $schemafy_core_instance;
    if (!$schemafy_core_instance || !method_exists($schemafy_core_instance, 'get_database')) {
        wp_send_json_error(__('Database not available', 'schemafy'));
    }

    $db = $schemafy_core_instance->get_database();

    $result = $db->delete_schema($id);

    if ($result !== false) {
        wp_send_json_success(array('message' => __('Schema deleted', 'schemafy')));
    } else {
        wp_send_json_error(__('Failed to delete schema', 'schemafy'));
    }
}
add_action('wp_ajax_schemafy_delete_schema', 'schemafy_ajax_delete_schema');

/**
 * AJAX Handler: Duplicate schema
 */
function schemafy_ajax_duplicate_schema() {
    // Check permissions
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Unauthorized', 'schemafy'));
    }

    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'schemafy_nonce')) {
        wp_send_json_error(__('Invalid nonce', 'schemafy'));
    }

    // Get schema ID
    $id = isset($_POST['schema_id']) ? intval($_POST['schema_id']) : (isset($_POST['id']) ? intval($_POST['id']) : 0);
    if (!$id) {
        wp_send_json_error(__('Invalid ID', 'schemafy'));
    }

    global $schemafy_core_instance;
    if (!$schemafy_core_instance || !method_exists($schemafy_core_instance, 'get_database')) {
        wp_send_json_error(__('Database not available', 'schemafy'));
    }

    $db = $schemafy_core_instance->get_database();

    // Duplicate the schema
    $new_id = $db->duplicate_schema($id);

    if ($new_id) {
        // Get the duplicated schema to return its data
        $new_schema = $db->get_schema($new_id);

        wp_send_json_success(array(
            'message' => __('Schema duplicated successfully', 'schemafy'),
            'new_id' => $new_id,
            'schema' => $new_schema
        ));
    } else {
        wp_send_json_error(__('Failed to duplicate schema', 'schemafy'));
    }
}
add_action('wp_ajax_schemafy_duplicate_schema', 'schemafy_ajax_duplicate_schema');


/**
 * Import schemas from CSV
 */
function schemafy_ajax_import_schemas_csv() {
    // Check permissions
    if (!current_user_can('manage_options')) {
        wp_send_json_error(array('message' => 'Unauthorized'));
    }

    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'schemafy_nonce')) {
        wp_send_json_error(array('message' => 'Invalid nonce'));
    }

    // JS sends parsed schemas as JSON string
    if (!isset($_POST['schemas']) || empty($_POST['schemas'])) {
        wp_send_json_error(array('message' => __('No schema data provided', 'schemafy')));
    }

    // Raw import payload: a JSON array of rows. It cannot be sanitized as a
    // string (that would corrupt the JSON); instead every field extracted from
    // each row below is individually sanitized — schema_name/url/schema_type via
    // dedicated sanitizers, and schema_data via decode + schemafy_sanitize_json_recursive.
    $schemas_json = wp_unslash($_POST['schemas']); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- decoded then each field sanitized per-row below
    $schemas = json_decode($schemas_json, true);

    if (!is_array($schemas) || empty($schemas)) {
        wp_send_json_error(array('message' => __('Invalid schema data format', 'schemafy')));
    }

    global $schemafy_core_instance;
    $db = $schemafy_core_instance->get_database();

    $imported = 0;
    $skipped = 0;
    $errors = array();

    foreach ($schemas as $index => $schema) {
        $row_num = $index + 2; // +2 for human-readable row (1-based + header)

        $schema_name = isset($schema['schema_name']) ? sanitize_text_field($schema['schema_name']) : '';
        $url = isset($schema['url']) ? esc_url_raw($schema['url']) : '';
        $schema_type = isset($schema['schema_type']) ? sanitize_text_field($schema['schema_type']) : '';
        $schema_data = isset($schema['schema_data']) ? $schema['schema_data'] : '';
        $is_active_raw = isset($schema['is_active']) ? $schema['is_active'] : 'Active';
        $is_active = (strtolower($is_active_raw) === 'inactive') ? 0 : 1;

        // Validate required fields
        if (empty($schema_name) || empty($url) || empty($schema_data)) {
            // translators: %d is a value inserted at runtime.
            $errors[] = sprintf(__('Row %d: Missing required fields (name, url, or schema data)', 'schemafy'), $row_num);
            $skipped++;
            continue;
        }

        // Validate and sanitize JSON in schema_data
        $decoded = json_decode($schema_data, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            // translators: %d is a value inserted at runtime.
            $errors[] = sprintf(__('Row %d: Invalid JSON in schema data', 'schemafy'), $row_num);
            $skipped++;
            continue;
        }
        $schema_data = wp_json_encode(schemafy_sanitize_json_recursive($decoded));

        // Create schema
        $data = array(
            'schema_name' => $schema_name,
            'url' => $url,
            'schema_type' => $schema_type,
            'schema_data' => $schema_data,
            'is_active' => $is_active,
            'created_at' => current_time('mysql'),
            'created_by' => get_current_user_id(),
            'updated_at' => current_time('mysql')
        );

        $result = $db->create_schema($data);

        if ($result) {
            $imported++;
        } else {
            // translators: %d is a value inserted at runtime.
            $errors[] = sprintf(__('Row %d: Failed to save to database', 'schemafy'), $row_num);
            $skipped++;
        }
    }

    wp_send_json_success(array(
        'imported' => $imported,
        'skipped' => $skipped,
        'errors' => $errors,
        'message' => sprintf(
            // translators: %1$d, %2$d are values inserted at runtime.
            __('%1$d schemas imported successfully, %2$d skipped', 'schemafy'),
            $imported,
            $skipped
        )
    ));
}
add_action('wp_ajax_schemafy_import_schemas_csv', 'schemafy_ajax_import_schemas_csv');

/**
 * Get schemas for a specific URL
 */
function schemafy_ajax_get_schemas_by_url() {
    // Check permissions
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Unauthorized', 'schemafy'));
    }

    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'schemafy_nonce')) {
        wp_send_json_error(__('Invalid nonce', 'schemafy'));
    }

    $url = isset($_POST['url']) ? esc_url_raw(wp_unslash($_POST['url'])) : '';

    if (empty($url)) {
        wp_send_json_error(__('URL is required', 'schemafy'));
    }

    global $schemafy_core_instance;
    $db = $schemafy_core_instance->get_database();

    // Get schemas for this URL
    $schemas = $db->get_schemas_by_url($url);

    if ($schemas === false) {
        wp_send_json_error(__('Error fetching schemas', 'schemafy'));
    }

    // Add creator name to each schema (handle missing created_by for old schemas)
    if (is_array($schemas)) {
        foreach ($schemas as &$schema) {
            $created_by = isset($schema['created_by']) ? intval($schema['created_by']) : 0;
            if ($created_by > 0) {
                $user = get_userdata($created_by);
                $schema['created_by_name'] = $user ? $user->display_name : '';
            } else {
                $schema['created_by_name'] = '';
            }
            // Ensure created_at exists
            if (!isset($schema['created_at'])) {
                $schema['created_at'] = '';
            }
            // Ensure updated_at exists
            if (!isset($schema['updated_at'])) {
                $schema['updated_at'] = '';
            }
        }
    }

    wp_send_json_success(array(
        'url' => $url,
        'schemas' => $schemas ? $schemas : array(),
        'count' => is_array($schemas) ? count($schemas) : 0
    ));
}
add_action('wp_ajax_schemafy_get_schemas_by_url', 'schemafy_ajax_get_schemas_by_url');

/**
 * Save new snippet from URL detail view
 */
function schemafy_ajax_save_snippet() {
    // Check permissions
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Unauthorized', 'schemafy'));
    }

    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'schemafy_nonce')) {
        wp_send_json_error(__('Invalid nonce', 'schemafy'));
    }

    // Get data
    $url = isset($_POST['url']) ? esc_url_raw(wp_unslash($_POST['url'])) : '';
    $schema_name = sanitize_text_field(schemafy_post_string('schema_name'));
    $schema_type = sanitize_text_field(schemafy_post_string('schema_type'));
    $schema_data = schemafy_post_string('schema_data');

    // Validate required fields
    if (empty($url) || empty($schema_name) || empty($schema_type) || empty($schema_data)) {
        wp_send_json_error(__('All fields are required', 'schemafy'));
    }

    // Validate and sanitize JSON
    $json_decoded = json_decode($schema_data, true);
    if (json_last_error() !== JSON_ERROR_NONE) {
        wp_send_json_error(__('Invalid JSON data', 'schemafy'));
    }
    $json_decoded = schemafy_sanitize_json_recursive($json_decoded);
    $schema_data = wp_json_encode($json_decoded);

    global $schemafy_core_instance;
    $db = $schemafy_core_instance->get_database();

    // Insert schema
    $result = $db->create_schema(array(
        'url' => $url,
        'schema_name' => $schema_name,
        'schema_type' => $schema_type,
        'schema_data' => $schema_data,
        'is_active' => 1,
        'created_at' => current_time('mysql'),
        'created_by' => get_current_user_id(),
        'updated_at' => current_time('mysql')
    ));

    if ($result === false) {
        wp_send_json_error(__('Error saving snippet', 'schemafy'));
    }

    wp_send_json_success(array(
        'schema_id' => $result,
        'message' => __('Snippet saved successfully', 'schemafy')
    ));
}
add_action('wp_ajax_schemafy_save_snippet', 'schemafy_ajax_save_snippet');

/**
 * Update existing snippet from URL detail view
 */
function schemafy_ajax_update_snippet() {
    // Check permissions
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Unauthorized', 'schemafy'));
    }

    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'schemafy_nonce')) {
        wp_send_json_error(__('Invalid nonce', 'schemafy'));
    }

    // Get data
    $schema_id = isset($_POST['schema_id']) ? intval($_POST['schema_id']) : 0;
    $schema_name = sanitize_text_field(schemafy_post_string('schema_name'));
    $schema_type = sanitize_text_field(schemafy_post_string('schema_type'));
    $schema_data = schemafy_post_string('schema_data');

    // Validate required fields
    if (empty($schema_id) || empty($schema_name) || empty($schema_type) || empty($schema_data)) {
        wp_send_json_error(__('All fields are required', 'schemafy'));
    }

    // Validate and sanitize JSON
    $json_decoded = json_decode($schema_data, true);
    if (json_last_error() !== JSON_ERROR_NONE) {
        wp_send_json_error(__('Invalid JSON data', 'schemafy'));
    }
    $json_decoded = schemafy_sanitize_json_recursive($json_decoded);
    $schema_data = wp_json_encode($json_decoded);

    global $schemafy_core_instance;
    $db = $schemafy_core_instance->get_database();

    // Update schema
    $result = $db->update_schema($schema_id, array(
        'schema_name' => $schema_name,
        'schema_type' => $schema_type,
        'schema_data' => $schema_data
    ));

    if ($result === false) {
        wp_send_json_error(__('Error updating snippet', 'schemafy'));
    }

    wp_send_json_success(array(
        'schema_id' => $schema_id,
        'message' => __('Snippet updated successfully', 'schemafy')
    ));
}
add_action('wp_ajax_schemafy_update_snippet', 'schemafy_ajax_update_snippet');

/**
 * Get single snippet for editing
 */
function schemafy_ajax_get_snippet() {
    // Check permissions
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Unauthorized', 'schemafy'));
    }

    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'schemafy_nonce')) {
        wp_send_json_error(__('Invalid nonce', 'schemafy'));
    }

    $schema_id = isset($_POST['schema_id']) ? intval($_POST['schema_id']) : 0;

    if (empty($schema_id)) {
        wp_send_json_error(__('Schema ID is required', 'schemafy'));
    }

    global $schemafy_core_instance;
    $db = $schemafy_core_instance->get_database();

    // Get schema
    $schema = $db->get_schema($schema_id);

    if ($schema === false || empty($schema)) {
        wp_send_json_error(__('Schema not found', 'schemafy'));
    }

    wp_send_json_success($schema);
}
add_action('wp_ajax_schemafy_get_snippet', 'schemafy_ajax_get_snippet');

/**
 * Bulk update schemas (activate/deactivate multiple)
 */
function schemafy_ajax_bulk_update_schemas() {
    // Check permissions
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Unauthorized', 'schemafy'));
    }

    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'schemafy_nonce')) {
        wp_send_json_error(__('Invalid nonce', 'schemafy'));
    }

    // Validate inputs
    if (!isset($_POST['schema_ids']) || !is_array($_POST['schema_ids']) || empty($_POST['schema_ids'])) {
        wp_send_json_error(__('No schemas selected', 'schemafy'));
    }

    if (!isset($_POST['updates']) || !is_array($_POST['updates'])) {
        wp_send_json_error(__('Invalid update data', 'schemafy'));
    }

    global $schemafy_core_instance;
    $db = $schemafy_core_instance->get_database();

    $schema_ids = array_map('intval', (array) wp_unslash($_POST['schema_ids']));
    $updates = schemafy_sanitize_schema_updates(wp_unslash($_POST['updates'])); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized field-by-field in schemafy_sanitize_schema_updates()
    $success_count = 0;

    foreach ($schema_ids as $schema_id) {
        $result = $db->update_schema($schema_id, $updates);
        if ($result) {
            $success_count++;
        }
    }

    if ($success_count > 0) {
        wp_send_json_success(array(
            // translators: %d is a value inserted at runtime.
            'message' => sprintf(__('%d schema(s) updated successfully', 'schemafy'), $success_count),
            'updated_count' => $success_count
        ));
    } else {
        wp_send_json_error(__('No schemas were updated', 'schemafy'));
    }
}
add_action('wp_ajax_schemafy_bulk_update_schemas', 'schemafy_ajax_bulk_update_schemas');

/**
 * Bulk delete schemas
 */
function schemafy_ajax_bulk_delete_schemas() {
    // Check permissions
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Unauthorized', 'schemafy'));
    }

    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'schemafy_nonce')) {
        wp_send_json_error(__('Invalid nonce', 'schemafy'));
    }

    // Validate inputs
    if (!isset($_POST['schema_ids']) || !is_array($_POST['schema_ids']) || empty($_POST['schema_ids'])) {
        wp_send_json_error(__('No schemas selected', 'schemafy'));
    }

    global $schemafy_core_instance;
    $db = $schemafy_core_instance->get_database();

    $schema_ids = array_map('intval', $_POST['schema_ids']);
    $success_count = 0;

    foreach ($schema_ids as $schema_id) {
        $result = $db->delete_schema($schema_id);
        if ($result) {
            $success_count++;
        }
    }

    if ($success_count > 0) {
        wp_send_json_success(array(
            // translators: %d is a value inserted at runtime.
            'message' => sprintf(__('%d schema(s) deleted successfully', 'schemafy'), $success_count),
            'deleted_count' => $success_count
        ));
    } else {
        wp_send_json_error(__('No schemas were deleted', 'schemafy'));
    }
}
add_action('wp_ajax_schemafy_bulk_delete_schemas', 'schemafy_ajax_bulk_delete_schemas');

/**
 * Bulk assign URLs to schemas
 */
function schemafy_ajax_bulk_assign_urls() {
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Unauthorized', 'schemafy'));
    }

    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'schemafy_nonce')) {
        wp_send_json_error(__('Invalid nonce', 'schemafy'));
    }

    if (!isset($_POST['schema_ids']) || !is_array($_POST['schema_ids']) || empty($_POST['schema_ids'])) {
        wp_send_json_error(__('No schemas selected', 'schemafy'));
    }

    $url = isset($_POST['value']) ? esc_url_raw(wp_unslash($_POST['value'])) : '';
    if (empty($url)) {
        wp_send_json_error(__('No URL provided', 'schemafy'));
    }

    global $schemafy_core_instance;
    $db = $schemafy_core_instance->get_database();

    $schema_ids = array_map('intval', $_POST['schema_ids']);
    $success_count = 0;

    foreach ($schema_ids as $schema_id) {
        $result = $db->update_schema($schema_id, array('url' => $url));
        if ($result) {
            $success_count++;
        }
    }

    if ($success_count > 0) {
        wp_send_json_success(array(
            // translators: %d is a value inserted at runtime.
            'message' => sprintf(__('%d schema(s) reassigned successfully', 'schemafy'), $success_count),
            'updated' => $success_count
        ));
    } else {
        wp_send_json_error(__('No schemas were updated', 'schemafy'));
    }
}
add_action('wp_ajax_schemafy_bulk_assign_urls', 'schemafy_ajax_bulk_assign_urls');

/**
 * Bulk edit schema properties
 * Supports: direct DB columns (is_active, schema_name, meta_title, meta_description)
 * and JSON fields inside schema_data (author, publisher, organization, description, image)
 */
function schemafy_ajax_bulk_edit_properties() {
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Unauthorized', 'schemafy'));
    }

    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'schemafy_nonce')) {
        wp_send_json_error(__('Invalid nonce', 'schemafy'));
    }

    if (!isset($_POST['schema_ids']) || !is_array($_POST['schema_ids']) || empty($_POST['schema_ids'])) {
        wp_send_json_error(__('No schemas selected', 'schemafy'));
    }

    if (!isset($_POST['updates']) || !is_array($_POST['updates'])) {
        wp_send_json_error(__('No updates provided', 'schemafy'));
    }

    global $wpdb, $schemafy_core_instance;
    $db = $schemafy_core_instance->get_database();
    $table_name = $db->get_table_name();

    $schema_ids = array_map('intval', (array) wp_unslash($_POST['schema_ids']));
    $updates = schemafy_sanitize_schema_updates(wp_unslash($_POST['updates'])); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized field-by-field in schemafy_sanitize_schema_updates()
    $success_count = 0;

    foreach ($schema_ids as $schema_id) {
        $schema = $db->get_schema($schema_id);
        if (!$schema) {
            continue;
        }

        $db_updates = array();

        // --- Direct DB column updates ---
        if (isset($updates['is_active'])) {
            $db_updates['is_active'] = intval($updates['is_active']);
        }
        if (isset($updates['schema_name']) && $updates['schema_name'] !== '') {
            $db_updates['schema_name'] = sanitize_text_field($updates['schema_name']);
        }
        if (isset($updates['meta_title']) && $updates['meta_title'] !== '') {
            $db_updates['meta_title'] = sanitize_text_field($updates['meta_title']);
        }
        if (isset($updates['meta_description']) && $updates['meta_description'] !== '') {
            $db_updates['meta_description'] = sanitize_text_field($updates['meta_description']);
        }

        // --- JSON schema_data field updates ---
        $json_fields = array('author', 'publisher', 'organization', 'description', 'image');
        $has_json_updates = false;
        foreach ($json_fields as $field) {
            if (isset($updates[$field]) && $updates[$field] !== '') {
                $has_json_updates = true;
                break;
            }
        }

        if ($has_json_updates) {
            $schema_data = json_decode($schema->schema_data, true);
            if (is_array($schema_data)) {
                // Author
                if (isset($updates['author']) && $updates['author'] !== '') {
                    $author_name = sanitize_text_field($updates['author']);
                    if (isset($schema_data['author']) && is_array($schema_data['author'])) {
                        $schema_data['author']['name'] = $author_name;
                    } else {
                        $schema_data['author'] = array(
                            '@type' => 'Person',
                            'name' => $author_name
                        );
                    }
                }

                // Publisher
                if (isset($updates['publisher']) && $updates['publisher'] !== '') {
                    $publisher_name = sanitize_text_field($updates['publisher']);
                    if (isset($schema_data['publisher']) && is_array($schema_data['publisher'])) {
                        $schema_data['publisher']['name'] = $publisher_name;
                    } else {
                        $schema_data['publisher'] = array(
                            '@type' => 'Organization',
                            'name' => $publisher_name
                        );
                    }
                }

                // Organization
                if (isset($updates['organization']) && $updates['organization'] !== '') {
                    $org_name = sanitize_text_field($updates['organization']);
                    if (isset($schema_data['@type']) && $schema_data['@type'] === 'Organization') {
                        $schema_data['name'] = $org_name;
                    }
                    if (isset($schema_data['publisher']) && is_array($schema_data['publisher'])) {
                        $schema_data['publisher']['name'] = $org_name;
                    }
                }

                // Description
                if (isset($updates['description']) && $updates['description'] !== '') {
                    $schema_data['description'] = sanitize_textarea_field($updates['description']);
                }

                // Image
                if (isset($updates['image']) && $updates['image'] !== '') {
                    $image_url = esc_url_raw($updates['image']);
                    if (isset($schema_data['image']) && is_array($schema_data['image'])) {
                        $schema_data['image']['url'] = $image_url;
                    } else {
                        $schema_data['image'] = $image_url;
                    }
                }

                $encoded = wp_json_encode($schema_data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
                if ($encoded !== false) {
                    $db_updates['schema_data'] = $encoded;
                } else {
                    schemafy_log('[SCHEMAFY Bulk Edit] JSON encode failed for schema #' . $schema_id);
                }
            } else {
                schemafy_log('[SCHEMAFY Bulk Edit] JSON decode failed for schema #' . $schema_id);
                // Don't skip - still apply direct DB column updates below
            }
        }

        if (!empty($db_updates)) {
            // Update the timestamp
            $wpdb->update(
                $table_name,
                array('updated_at' => current_time('mysql')),
                array('id' => $schema_id),
                array('%s'),
                array('%d')
            );

            $result = $db->update_schema($schema_id, $db_updates);
            if ($result) {
                $success_count++;
            }
        }
    }

    if ($success_count > 0) {
        wp_send_json_success(array(
            // translators: %d is a value inserted at runtime.
            'message' => sprintf(__('%d schema(s) updated successfully', 'schemafy'), $success_count),
            'updated' => $success_count
        ));
    } else {
        wp_send_json_error(__('No schemas were updated', 'schemafy'));
    }
}
add_action('wp_ajax_schemafy_bulk_edit_properties', 'schemafy_ajax_bulk_edit_properties');

/**
 * Bulk duplicate schemas
 */
function schemafy_ajax_bulk_duplicate_schemas() {
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Unauthorized', 'schemafy'));
    }

    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'schemafy_nonce')) {
        wp_send_json_error(__('Invalid nonce', 'schemafy'));
    }

    if (!isset($_POST['schema_ids']) || !is_array($_POST['schema_ids']) || empty($_POST['schema_ids'])) {
        wp_send_json_error(__('No schemas selected', 'schemafy'));
    }

    global $schemafy_core_instance;
    $db = $schemafy_core_instance->get_database();

    $schema_ids = array_map('intval', $_POST['schema_ids']);
    $success_count = 0;

    foreach ($schema_ids as $schema_id) {
        $result = $db->duplicate_schema($schema_id);
        if ($result) {
            $success_count++;
        }
    }

    if ($success_count > 0) {
        wp_send_json_success(array(
            // translators: %d is a value inserted at runtime.
            'message' => sprintf(__('%d schema(s) duplicated successfully', 'schemafy'), $success_count),
            'duplicated' => $success_count
        ));
    } else {
        wp_send_json_error(__('No schemas were duplicated', 'schemafy'));
    }
}
add_action('wp_ajax_schemafy_bulk_duplicate_schemas', 'schemafy_ajax_bulk_duplicate_schemas');



/**
 * Get all published URLs from the site for dropdown
 * Returns pages, posts, and custom post types with additional metadata
 */
function schemafy_ajax_get_site_urls() {
    // Check permissions
    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Unauthorized', 'schemafy'));
    }

    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'schemafy_nonce')) {
        wp_send_json_error(__('Invalid nonce', 'schemafy'));
    }

    $search = isset($_POST['search']) ? sanitize_text_field(wp_unslash($_POST['search'])) : '';
    $post_type = isset($_POST['post_type']) ? sanitize_text_field(wp_unslash($_POST['post_type'])) : 'any';

    // Get all public post types
    $post_types = get_post_types(array('public' => true), 'names');

    if ($post_type !== 'any' && in_array($post_type, $post_types)) {
        $post_types = array($post_type);
    }

    // Query args
    $args = array(
        'post_type' => $post_types,
        'post_status' => 'publish',
        'posts_per_page' => 500, // Limit to 500 for performance
        'orderby' => 'modified',
        'order' => 'DESC',
        'no_found_rows' => true,
        'update_post_meta_cache' => false,
        'update_post_term_cache' => false
    );

    // Add search if provided
    if (!empty($search)) {
        $args['s'] = $search;
        $args['posts_per_page'] = 100; // Limit search results
    }

    $query = new WP_Query($args);
    $urls = array();

    foreach ($query->posts as $post) {
        $permalink = get_permalink($post->ID);

        // Get post type label
        $post_type_obj = get_post_type_object($post->post_type);
        $post_type_label = $post_type_obj ? $post_type_obj->labels->singular_name : $post->post_type;

        $urls[] = array(
            'id' => $post->ID,
            'title' => $post->post_title,
            'url' => $permalink,
            'post_type' => $post->post_type,
            'post_type_label' => $post_type_label,
            'modified' => get_the_modified_date('Y-m-d H:i', $post->ID)
        );
    }

    // Add homepage
    if (empty($search) || stripos(get_bloginfo('name'), $search) !== false) {
        array_unshift($urls, array(
            'id' => 0,
            'title' => get_bloginfo('name') . ' - Homepage',
            'url' => home_url('/'),
            'post_type' => 'homepage',
            'post_type_label' => 'Homepage',
            'modified' => current_time('Y-m-d H:i')
        ));
    }

    wp_send_json_success(array(
        'urls' => $urls,
        'count' => count($urls),
        'search' => $search
    ));
}
add_action('wp_ajax_schemafy_get_site_urls', 'schemafy_ajax_get_site_urls');

/**
 * Dismiss onboarding tutorial for current user
 */
function schemafy_ajax_dismiss_onboarding() {
    // Debug logging
    schemafy_log('[SCHEMAFY Onboarding] Dismiss request received');

    // Check permissions
    if (!current_user_can('manage_options')) {
        schemafy_log('[SCHEMAFY Onboarding] Permission denied');
        wp_send_json_error(__('Unauthorized', 'schemafy'));
    }

    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'schemafy_dismiss_onboarding')) {
        schemafy_log('[SCHEMAFY Onboarding] Invalid nonce');
        wp_send_json_error(__('Invalid nonce', 'schemafy'));
    }

    $user_id = get_current_user_id();
    schemafy_log('[SCHEMAFY Onboarding] User ID: ' . $user_id);

    // Save user meta
    $result = update_user_meta($user_id, 'schemafy_onboarding_dismissed', '1');
    schemafy_log('[SCHEMAFY Onboarding] update_user_meta result: ' . ($result !== false ? 'success (meta_id: ' . $result . ')' : 'false (already exists or failed)'));

    // Verify it was saved
    $verify = get_user_meta($user_id, 'schemafy_onboarding_dismissed', true);
    schemafy_log('[SCHEMAFY Onboarding] Verification read: ' . $verify);

    wp_send_json_success(array(
        'message' => __('Onboarding dismissed', 'schemafy'),
        'user_id' => $user_id,
        'saved' => $verify === '1'
    ));
}
add_action('wp_ajax_schemafy_dismiss_onboarding', 'schemafy_ajax_dismiss_onboarding');

/**
 * Reset onboarding tutorial for current user (allows restarting tutorial)
 */
function schemafy_ajax_reset_onboarding() {
    // Debug logging
    schemafy_log('[SCHEMAFY Onboarding] Reset request received');

    // Check permissions
    if (!current_user_can('manage_options')) {
        schemafy_log('[SCHEMAFY Onboarding] Reset permission denied');
        wp_send_json_error(__('Unauthorized', 'schemafy'));
    }

    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'schemafy_reset_onboarding')) {
        schemafy_log('[SCHEMAFY Onboarding] Reset invalid nonce');
        wp_send_json_error(__('Invalid nonce', 'schemafy'));
    }

    $user_id = get_current_user_id();
    schemafy_log('[SCHEMAFY Onboarding] Resetting for User ID: ' . $user_id);

    // Delete the user meta to reset onboarding
    $result = delete_user_meta($user_id, 'schemafy_onboarding_dismissed');
    schemafy_log('[SCHEMAFY Onboarding] delete_user_meta result: ' . ($result ? 'success' : 'failed or did not exist'));

    // Verify it was deleted
    $verify = get_user_meta($user_id, 'schemafy_onboarding_dismissed', true);
    schemafy_log('[SCHEMAFY Onboarding] Verification after reset: "' . $verify . '" (should be empty)');

    wp_send_json_success(array(
        'message' => __('Tutorial will restart when you reload the page', 'schemafy'),
        'user_id' => $user_id,
        'reset' => empty($verify)
    ));
}
add_action('wp_ajax_schemafy_reset_onboarding', 'schemafy_ajax_reset_onboarding');


