<?php

namespace Pixeldev\SquareWooSync\REST;

use Error;
use Pixeldev\SquareWooSync\Abstracts\RESTController;
use Pixeldev\SquareWooSync\Square\SquareHelper;
use WP_REST_Server;
use WP_REST_Request;
use WP_REST_Response;
use WP_Error;


if (!defined('ABSPATH')) {
  exit; // Exit if accessed directly
}

class CustomersController extends RESTController
{
  protected $namespace = 'sws/v1';
  protected $base = 'customers';

  public function register_routes()
  {
    register_rest_route(
      $this->namespace,
      '/' . $this->base,
      [
        [
          'methods'             => WP_REST_Server::READABLE,
          'callback'            => [$this, 'get_customers'],
          'permission_callback' => [$this, 'check_permission'],
        ],
        [
          'methods'             => WP_REST_Server::CREATABLE,
          'callback'            => [$this, 'handle_process_customers'],
          'permission_callback' => [$this, 'check_permission'],
        ],
      ]
    );

    register_rest_route(
      $this->namespace,
      '/' . $this->base . '/search',
      [
        [
          'methods'             => WP_REST_Server::READABLE,
          'callback'            => [$this, 'search_customers'],
          'permission_callback' => [$this, 'check_permission'],
        ],
      ]
    );

    register_rest_route(
      $this->namespace,
      '/' . $this->base . '/saved-customers',
      [
        [
          'methods'             => WP_REST_Server::READABLE,
          'callback'            => [$this, 'get_saved_customers'],
          'permission_callback' => [$this, 'check_permission'],
        ],
      ]
    );

    register_rest_route(
      $this->namespace,
      '/' . $this->base . '/match',
      [
        [
          'methods'             => WP_REST_Server::READABLE,
          'callback'            => [$this, 'match_customers'],
          'permission_callback' => [$this, 'check_permission'],
        ],
      ]
    );

    register_rest_route(
      $this->namespace,
      '/' . $this->base . '/get-groups',
      [
        [
          'methods'             => WP_REST_Server::READABLE,
          'callback'            => [$this, 'get_groups'],
          'permission_callback' => [$this, 'check_permission'],
        ],
      ]
    );

    register_rest_route(
      $this->namespace,
      '/' . $this->base . '/groups-segments',
      [
        [
          'methods'             => WP_REST_Server::READABLE,
          'callback'            => [$this, 'get_groups_segments'],
          'permission_callback' => [$this, 'check_permission'],
        ],
      ]
    );

    register_rest_route(
      $this->namespace,
      '/' . $this->base . '/role-mappings',
      [
        [
          'methods'             => WP_REST_Server::READABLE,
          'callback'            => [$this, 'get_role_mappings'],
          'permission_callback' => [$this, 'check_permission'],
        ],
        [
          'methods'             => WP_REST_Server::CREATABLE,
          'callback'            => [$this, 'set_role_mappings'],
          'permission_callback' => [$this, 'check_permission'],
          'args'                => [
            'roleMappings' => [
              'required' => true,
              'validate_callback' => function ($param, $request, $key) {
                return is_array($param);
              }
            ],
          ],
        ],
      ]
    );

    register_rest_route(
      $this->namespace,
      '/' . $this->base . '/import',
      [
        'methods'             => WP_REST_Server::CREATABLE,
        'callback'            => [$this, 'import_customers'],
        'permission_callback' => [$this, 'check_permission'],
      ]
    );

    register_rest_route(
      $this->namespace,
      '/' . $this->base . '/import/status',
      [
        'methods'             => WP_REST_Server::READABLE,
        'callback'            => [$this, 'get_import_status'],
        'permission_callback' => [$this, 'check_permission'],
      ]
    );

    register_rest_route(
      $this->namespace,
      '/' . $this->base . '/import/stop',
      [
        'methods'             => WP_REST_Server::CREATABLE,
        'callback'            => [$this, 'stop_import'],
        'permission_callback' => [$this, 'check_permission'],
      ]
    );
  }


  /**************************************************************************
   * 2) import_customers (POST)
   *    Receives all IDs (or empty array) plus importData and batchSize
   *    Schedules chunked jobs via Action Scheduler
   **************************************************************************/
  public function import_customers(WP_REST_Request $request)
  {
    global $wpdb;

    $params     = $request->get_json_params();
    $ids        = isset($params['ids']) ? (array) $params['ids'] : [];
    $importData = isset($params['importData']) ? (array) $params['importData'] : [];
    $batchSize  = !empty($params['batchSize']) ? (int) $params['batchSize'] : 10;

    // 1) If no IDs passed => fetch all from "square_woo_customers" table.
    if (empty($ids)) {
      $table_name = $wpdb->prefix . 'square_woo_customers';
      // Grab all IDs from the DB
      $ids = $wpdb->get_col("SELECT id FROM {$table_name}");
    }


    $total = count($ids);

    // Mark as importing in plugin settings
    $settings = get_option('square-woo-sync_settings', []);
    if (!isset($settings['customersImport'])) {
      $settings['customersImport'] = [];
    }

    // If already importing, return error
    if (!empty($settings['customersImport']['isImporting'])) {
      return new WP_REST_Response([
        'error'   => 'An import is already in progress.',
        'message' => 'Wait for it to complete or stop it first.'
      ], 409);
    }

    $settings['customersImport']['isImporting'] = true;
    $settings['customersImport']['progress'] = [
      'total'     => $total,
      'processed' => 0,
      'results'   => [],
      'started'   => current_time('mysql'),
      'finished'  => false,
      'error'     => ''
    ];
    update_option('square-woo-sync_settings', $settings);

    // If no IDs => we do nothing, but mark progress as done
    if ($total === 0) {
      $settings['customersImport']['isImporting'] = false;
      $settings['customersImport']['progress']['processed'] = 0;
      $settings['customersImport']['progress']['finished']  = true;
      $settings['customersImport']['progress']['error']     = 'No IDs provided; import ended.';
      update_option('square-woo-sync_settings', $settings);

      return new WP_REST_Response([
        'message' => 'No IDs provided, import ended.',
        'total'   => 0,
        'status'  => 'done'
      ], 200);
    }

    // Break the IDs into chunks
    $chunks = array_chunk($ids, $batchSize);
    foreach ($chunks as $index => $chunk) {
      // We'll schedule an async action for each chunk
      $args = [
        'chunk_ids'   => $chunk,
        'import_data' => $importData
      ];
      // Use the Action Scheduler group: "square_customers_import" 
      // Make sure you have Action Scheduler or a fallback
      if (function_exists('as_enqueue_async_action')) {
        as_enqueue_async_action('sws_import_customers_chunk', $args, 'square_customers_import');
      } else {
        // Fallback or error if Action Scheduler isn't present
        return new WP_REST_Response([
          'error'   => 'Action Scheduler not found.',
          'message' => 'Cannot schedule chunked import.'
        ], 500);
      }
    }

    return new WP_REST_Response([
      'message' => 'Import scheduled in background',
      'total'   => $total,
      'batchSize' => $batchSize
    ], 200);
  }

  /**************************************************************************
   * 3) get_import_status (GET)
   *    Return isImporting + progress => front end can poll
   **************************************************************************/
  public function get_import_status(WP_REST_Request $request)
  {
    $settings = get_option('square-woo-sync_settings', []);
    if (!isset($settings['customersImport'])) {
      $settings['customersImport'] = [];
    }

    $isImporting = !empty($settings['customersImport']['isImporting']) ? true : false;
    $progress    = $settings['customersImport']['progress'] ?? null;

    return new WP_REST_Response([
      'isImporting' => $isImporting,
      'progress'    => $progress
    ], 200);
  }

  /**************************************************************************
   * 4) stop_import (POST)
   *    Cancels any ongoing import. Unschedule pending chunk jobs.
   **************************************************************************/
  public function stop_import(WP_REST_Request $request)
  {
    // Mark isImporting = false
    $settings = get_option('square-woo-sync_settings', []);
    if (!isset($settings['customersImport'])) {
      $settings['customersImport'] = [];
    }

    if (empty($settings['customersImport']['isImporting'])) {
      return new WP_REST_Response([
        'message' => 'No import in progress to stop.',
        'success' => false
      ], 200);
    }

    $settings['customersImport']['isImporting'] = false;
    if (!isset($settings['customersImport']['progress'])) {
      $settings['customersImport']['progress'] = [];
    }
    $settings['customersImport']['progress']['finished'] = true;
    $settings['customersImport']['progress']['error']    = 'Stopped by user';
    $settings['customersImport']['progress']['stopped']  = current_time('mysql');

    update_option('square-woo-sync_settings', $settings);

    // Unschedule all future chunk actions
    if (function_exists('as_unschedule_all_actions')) {
      as_unschedule_all_actions('sws_import_customers_chunk', [], 'square_customers_import');
    }

    return new WP_REST_Response([
      'message' => 'Import stopped',
      'success' => true
    ], 200);
  }

  /**************************************************************************
   * 5) The worker function that processes each chunk of IDs.
   *    We'll define a static method or public method. You must 
   *    hook it to 'sws_import_customers_chunk' in your plugin or theme.
   **************************************************************************/
  public static function process_customers_chunk($chunk_ids, $import_data)
  {
    // This is called by Action Scheduler. 
    // We'll update the progress, then call your existing logic
    // e.g. process_customers() for each ID or batch them. Up to you.

    $settings = get_option('square-woo-sync_settings', []);
    if (!isset($settings['customersImport'])) {
      $settings['customersImport'] = [];
    }
    if (empty($settings['customersImport']['isImporting'])) {
      // The import was probably stopped or not started
      return;
    }

    // We'll do each ID in a loop, calling the existing 'process_customers' logic item-by-item
    // Or we can pass the entire chunk to 'process_customers' if that is efficient.
    // For demonstration, let's do item-by-item:
    $controller = new self();

    foreach ($chunk_ids as $id) {
      // Call your existing process_customers method
      $result = $controller->process_customers([$id], $import_data);
      // $result = [ 'success' => [ 'Bob', 'Jane' ], 'failed' => [ maybe WP_Errors ] ]

      // Extract success names
      $successNames = $result['success'] ?? [];
      // Extract failures (could be WP_Error objects or strings)
      $failures = $result['failed'] ?? [];

      // For each success, add a log entry: "Synced user: X"
      foreach ($successNames as $name) {
        $settings['customersImport']['progress']['results'][] = [
          'status'  => 'SUCCESS',
          'message' => "Synced user: $name"
        ];
      }

      // For each failure, add a log entry: "Failed to sync user: {error message}"
      foreach ($failures as $err) {
        $errorMessage = '';
        if (is_wp_error($err)) {
          $errorMessage = $err->get_error_message();
        } elseif (is_string($err)) {
          $errorMessage = $err;
        } else {
          $errorMessage = 'Unknown error.';
        }
        $settings['customersImport']['progress']['results'][] = [
          'status'  => 'FAILED',
          'message' => "Failed to sync user: $errorMessage"
        ];
      }

      // Increment 'processed' count
      if (!isset($settings['customersImport']['progress']['processed'])) {
        $settings['customersImport']['progress']['processed'] = 0;
      }
      $settings['customersImport']['progress']['processed']++;

      // Save after each ID
      update_option('square-woo-sync_settings', $settings);
    }

    // If we have processed everything, we might check if we should set finished=true.
    // A simpler approach is: when all chunk actions are done, the final chunk might check if processed >= total
    $processed = $settings['customersImport']['progress']['processed'] ?? 0;
    $total     = $settings['customersImport']['progress']['total']     ?? 0;
    if ($processed >= $total) {
      $settings['customersImport']['isImporting'] = false;
      $settings['customersImport']['progress']['finished']   = true;
      $settings['customersImport']['progress']['finished_at'] = current_time('mysql');
      update_option('square-woo-sync_settings', $settings);
    }
  }

  public function handle_process_customers(WP_REST_Request $request)
  {
    $ids = $request->get_json_params()['ids'];
    $import_data = $request->get_json_params()['importData'];

    if (empty($ids) || !is_array($ids)) {
      return new WP_Error('invalid_ids', 'Invalid or missing IDs parameter', ['status' => 400]);
    }


    return new WP_REST_Response(['success' => true, 'message' => esc_html__('Customers processed', 'squarewoosync-pro'), 'data' => $this->process_customers($ids, $import_data)], 200);
  }

  /**
   * Our existing process_customers method (unchanged).
   * Called by the chunk worker for each item or chunk.
   */
  public function process_customers(array $ids, array $import_data)
  {
    global $wpdb;

    if (empty($ids) || !is_array($ids)) {
      return new WP_Error('invalid_ids', 'Invalid or missing IDs parameter');
    }

    // Possibly do your existing "match_customers" logic:
    $request = new WP_REST_Request();
    if (isset($import_data) && !empty($import_data['setRole'])) {
      $request->set_param('setrole', 'true');
    }
    $this->match_customers($request);

    $table_name = $wpdb->prefix . 'square_woo_customers';
    $new_square_customers = [];
    $errors = [];

    // Import Square Customers logic
    if (
      isset($import_data) && (
        !empty($import_data['squareToWoo']) ||
        !empty($import_data['wooToSquare']) ||
        !empty($import_data['sync'])
      )
    ) {
      foreach ($ids as $id) {
        $orig = $wpdb->get_row(
          $wpdb->prepare(
            "SELECT email FROM {$table_name} WHERE id = %d",
            $id
          ),
          ARRAY_A
        );
        if (! $orig || empty($orig['email'])) {
          continue;
        }
        $lower_email = strtolower($orig['email']);

        // 2) Fetch just id + has_loyalty for *all* rows with that email
        $rows = $wpdb->get_results(
          $wpdb->prepare(
            "SELECT id, has_loyalty 
                   FROM {$table_name} 
                  WHERE LOWER(email) = %s",
            $lower_email
          ),
          ARRAY_A
        );

        // 3) If any row has_loyalty = 1, but it’s *not* this $id, skip it
        $loyaltyId = null;
        foreach ($rows as $r) {
          if ((int) $r['has_loyalty'] === 1) {
            $loyaltyId = (int) $r['id'];
            break;
          }
        }
        if ($loyaltyId !== null && $loyaltyId !== (int) $id) {
          // a loyalty account exists elsewhere → skip this non-loyalty row
          continue;
        }

        // 4) Now load the *full* customer record (either the loyalty row itself,
        //    or if no loyalty exists, this first-seen row)
        $customer = $wpdb->get_row(
          $wpdb->prepare(
            "SELECT * FROM {$table_name} WHERE id = %d",
            $id
          ),
          ARRAY_A
        );
        if (! $customer) {
          continue;
        }

        if ($customer !== null) {
          if ($customer['source'] === 'Square' && !empty($import_data['squareToWoo'])) {
            $result = $this->create_wordpress_user($customer, $import_data);
            if (is_wp_error($result)) {
              $errors[] = $result;
            } else {
              $new_square_customers[] = $result;
            }
          } elseif ($customer['source'] === 'Woo' && !empty($import_data['wooToSquare'])) {
            $result = $this->create_square_user($customer, $import_data);
            if (is_wp_error($result)) {
              $errors[] = $result;
            } else {
              $new_square_customers[] = $result;
            }
          } elseif ($customer['source'] === 'Both' && !empty($import_data['sync'])) {
            $result = $this->sync_user($customer, $import_data);
            if (is_wp_error($result)) {
              $errors[] = $result;
            } else {
              $new_square_customers[] = $result;
            }
          }
        }
      }
    }

    // Return an array with success/failed for logging
    return [
      'success' => $new_square_customers,
      'failed'  => $errors,
    ];
  }

  private function sync_user(array $customer, array $import_data)
  {

    if ($import_data['source'] === 'WooCommerce') {
      return $this->update_square_user($customer, $import_data);
    }

    if ($import_data['source'] === 'Square') {
      return $this->update_woo_user($customer, $import_data);
    }

    return [];
  }

  private function update_woo_user(array $customer, array $import_data)
  {
    try {
      // Retrieve the Square customer ID from the customer data
      $square_customer_id = $customer['square_customer_id'];

      $squareHelper = new SquareHelper();
      $response = $squareHelper->square_api_request('/customers/' . $square_customer_id, 'GET');

      if (!$response['success']) {
        throw new \Exception('Failed to retrieve Square customer');
      }

      $square_customer = $response['data']['customer'];

      // Find the WordPress user by the Square customer ID
      $user_query = new \WP_User_Query([
        'meta_key' => 'square_customer_id',
        'meta_value' => $square_customer_id,
        'number' => 1,
      ]);

      $users = $user_query->get_results();

      if (empty($users)) {
        return new WP_Error('sync_user', 'Unable to find WordPress user with Square customer ID: ' . $square_customer_id);
      }

      $user = $users[0];
      $user_id = $user->ID;

      // Update user meta with Square customer data
      update_user_meta($user_id, 'first_name', $square_customer['given_name'] ?? '');
      update_user_meta($user_id, 'last_name', $square_customer['family_name'] ?? '');
      update_user_meta($user_id, 'billing_first_name', $square_customer['given_name'] ?? '');
      update_user_meta($user_id, 'billing_last_name', $square_customer['family_name'] ?? '');
      update_user_meta($user_id, 'billing_phone', $square_customer['phone_number'] ?? '');

      $address = $square_customer['address'] ?? [];
      update_user_meta($user_id, 'billing_address_1', $address['address_line_1'] ?? '');
      update_user_meta($user_id, 'billing_address_2', $address['address_line_2'] ?? '');
      update_user_meta($user_id, 'billing_city', $address['locality'] ?? '');
      update_user_meta($user_id, 'billing_postcode', $address['postal_code'] ?? '');
      update_user_meta($user_id, 'billing_country', $address['country'] ?? '');
      update_user_meta($user_id, 'billing_state', $address['administrative_district_level_1'] ?? '');

      // Get current roles to ensure we don't remove administrator roles
      $current_roles = $user->roles;

      // Set user roles
      $group_ids = array_map('trim', $square_customer['group_ids'] ?? []);
      $settings = get_option('square-woo-sync_settings', []);

      $primary_role = 'customer';
      $additional_roles = [];

      if (isset($import_data) && $import_data['setRole'] === true) {
        $role_mappings = $settings['customers']['roleMappings'] ?? [];
        $mapped_roles = [];
        foreach ($group_ids as $group_id) {
          foreach ($role_mappings as $role => $mapping) {
            if (isset($mapping['groupId']) && $mapping['groupId'] === $group_id) {
              $mapped_roles[$role] = $mapping['priority'] ?? PHP_INT_MAX;
            }
          }
        }

        if (!empty($mapped_roles)) {
          asort($mapped_roles); // Sort roles by priority, ascending
          $primary_role = key($mapped_roles); // The role with the lowest priority
          $additional_roles = array_keys(array_slice($mapped_roles, 1)); // Any additional roles
        }
      }

      if (!in_array('administrator', $current_roles)) {
        wp_update_user([
          'ID' => $user_id,
          'role' => $primary_role,
        ]);
      }

      // Add additional roles
      $user = new \WP_User($user_id);
      foreach ($additional_roles as $additional_role) {
        $user->add_role($additional_role);
      }

      // Ensure administrator role is not removed
      if (in_array('administrator', $current_roles) && !in_array('administrator', $user->roles)) {
        $user->add_role('administrator');
      }

      return $square_customer['given_name'] ?? $customer['first_name'];
    } catch (\Exception $e) {
      return new WP_Error('update_woo_user_error', $e->getMessage());
    }
  }


  private function update_square_user(array $customer, array $import_data)
  {
    try {
      $user = get_user_by('email', $customer['email']);

      if (!$user) {
        return new WP_Error('sync_user', 'Unable to find user with email: ' . $customer['email']);
      }

      $user_id = $user->ID;
      $first_name = get_user_meta($user_id, 'first_name', true);
      $last_name = get_user_meta($user_id, 'last_name', true);
      $billing_first_name = get_user_meta($user_id, 'billing_first_name', true);
      $billing_last_name = get_user_meta($user_id, 'billing_last_name', true);
      $billing_phone = get_user_meta($user_id, 'billing_phone', true);
      $billing_address_1 = get_user_meta($user_id, 'billing_address_1', true);
      $billing_address_2 = get_user_meta($user_id, 'billing_address_2', true);
      $billing_city = get_user_meta($user_id, 'billing_city', true);
      $billing_postcode = get_user_meta($user_id, 'billing_postcode', true);
      $billing_country = get_user_meta($user_id, 'billing_country', true);
      $billing_state = get_user_meta($user_id, 'billing_state', true);
      $roles = $user->roles;

      // Use billing first name and last name if first name and last name are not set
      $display_first_name = !empty($first_name) ? $first_name : $billing_first_name;
      $display_last_name = !empty($last_name) ? $last_name : $billing_last_name;

      // Get role mappings from settings
      $settings = get_option('square-woo-sync_settings', []);
      $role_mappings = $settings['customers']['roleMappings'] ?? [];

      // Determine group IDs based on user's roles
      $group_ids = [];
      foreach ($roles as $current_role) {
        foreach ($role_mappings as $role => $mapping) {
          if ($current_role === $role && isset($mapping['groupId'])) {
            $group_ids[] = sanitize_text_field($mapping['groupId']);
          }
        }
      }
      // Ensure group IDs are unique and not empty
      $group_ids = array_filter(array_unique($group_ids), function ($value) {
        return !empty($value) && $value !== 'N/A';
      });

      // Prepare payload for updating Square customer
      $payload = [];
      if (!empty($display_first_name)) $payload['given_name'] = $display_first_name;
      if (!empty($display_last_name)) $payload['family_name'] = $display_last_name;
      if (!empty($billing_phone)) $payload['phone_number'] = $billing_phone;

      $address = [];
      if (!empty($billing_address_1)) $address['address_line_1'] = $billing_address_1;
      if (!empty($billing_address_2)) $address['address_line_2'] = $billing_address_2;
      if (!empty($billing_city)) $address['locality'] = $billing_city;
      if (!empty($billing_postcode)) $address['postal_code'] = $billing_postcode;
      if (!empty($billing_country)) $address['country'] = $billing_country;
      if (!empty($billing_state)) $address['administrative_district_level_1'] = $billing_state;

      if (!empty($address)) $payload['address'] = $address;

      // Check if there is at least one field to update
      if (empty($payload)) {
        return new WP_Error('square_api_error', 'At least one field must be set to update a customer.');
      }

      $squareHelper = new SquareHelper();

      // Retrieve the Square customer ID from your user meta or elsewhere
      $square_customer_id = get_user_meta($user_id, 'square_customer_id', true);

      if (!$square_customer_id) {
        return new WP_Error('square_customer_id_missing', 'Square customer ID is missing for user.');
      }

      $response = $squareHelper->square_api_request('/customers/' . $square_customer_id, 'PUT', $payload);

      if (!$response['success']) {
        // Handle specific error response from Square API
        if (isset($response['data']['errors']) && is_array($response['data']['errors'])) {
          foreach ($response['data']['errors'] as $error) {
            if ($error['code'] === 'BAD_REQUEST' && $error['detail'] === 'At least one field must be set to update a customer.') {
              return new WP_Error('square_api_error', 'At least one field must be set to update a customer.');
            }
          }
        }
        throw new \Exception('Failed to update Square customer');
      }

      // Add customer to groups
      foreach ($group_ids as $group_id) {
        $group_response = $squareHelper->square_api_request('/customers/' . $square_customer_id . '/groups/' . $group_id, 'PUT');
        if (!$group_response['success']) {
          throw new \Exception('Failed to add Square customer to group');
        }
      }

      return $display_first_name; // Return display_first_name if success
    } catch (\Exception $e) {
      return new WP_Error('square_api_error', $e->getMessage());
    }
  }

  private function create_square_user(array $customer, array $import_data)
  {
    try {
      if (!isset($customer['email'])) {
        return new WP_Error('invalid_data', 'Email is required');
      }

      $email = sanitize_email($customer['email']);
      $first_name = isset($customer['first_name']) ? sanitize_text_field($customer['first_name']) : '';
      $last_name = isset($customer['last_name']) ? sanitize_text_field($customer['last_name']) : '';
      $group_ids = isset($customer['group_ids']) ? explode(',', sanitize_text_field($customer['group_ids'])) : [];

      // Generate a unique idempotency key
      $idempotency_key = uniqid('square_', true);

      $payload = [
        'idempotency_key' => $idempotency_key,
        'email_address' => $email,
        'given_name' => $first_name,
        'family_name' => $last_name
      ];

      // Retrieve WordPress user by email
      $user = get_user_by('email', $email);
      if ($user) {
        $current_roles = $user->roles;
      } else {
        $current_roles = [];
      }

      // Get role mappings from settings
      $settings = get_option('square-woo-sync_settings', []);
      $role_mappings = $settings['customers']['roleMappings'] ?? [];

      // Determine group IDs based on user's roles
      foreach ($current_roles as $current_role) {
        foreach ($role_mappings as $role => $mapping) {
          if ($current_role === $role && isset($mapping['groupId'])) {
            $group_ids[] = sanitize_text_field($mapping['groupId']);
          }
        }
      }

      // Ensure group IDs are unique and not empty
      $group_ids = array_filter(array_unique($group_ids), function ($value) {
        return !empty($value) && $value !== 'N/A';
      });


      $squareHelper = new SquareHelper();
      $response = $squareHelper->square_api_request('/customers', 'POST', $payload);

      if (!$response['success']) {
        return new WP_Error('square_api_error', 'Failed to create Square customer');
      }


      $square_customer_id = $response['data']['customer']['id'];

      // Add customer to groups
      foreach ($group_ids as $group_id) {
        $group_response = $squareHelper->square_api_request('/customers/' . $square_customer_id . '/groups/' . $group_id, 'PUT');
        if (!$group_response['success']) {
          return new WP_Error('square_api_group_error', 'Failed to add Square customer to group');
        }
      }

      update_user_meta($user->ID, 'square_customer_id', $square_customer_id);


      return $first_name;
    } catch (\Exception $e) {
      error_log('Error in create_square_user: ' . $e->getMessage());
      return new WP_Error('rest_create_square_user', esc_html($e->getMessage()));
    }
  }



  private function create_wordpress_user(array $customer, array $import_data)
  {
    try {
      global $wpdb;

      if (!isset($customer['email']) || !isset($customer['first_name'])) {
        return new WP_Error('invalid_data', 'email or name doesn\'t exist');
      }

      $email = sanitize_email($customer['email']);
      $name = sanitize_user(sanitize_text_field($customer['first_name']));
      $group_ids = array_map('trim', explode(',', sanitize_text_field($customer['group_ids'])));
      $square_id = sanitize_text_field($customer['square_customer_id']);

      if (email_exists($email)) {
        return new WP_Error('existing_user', 'A user with email: ' . $email . ' already exists');
      }

      $username = sanitize_user(strtolower(preg_replace('/[^a-zA-Z0-9._-]/', '', $name)));
      $original_name = $username;
      $suffix = 1;

      while (username_exists($username)) {
        $username = sanitize_user($original_name . '.' . $suffix);
        $suffix++;
      }

      $settings = get_option('square-woo-sync_settings', []);
      $role_mappings = $settings['customers']['roleMappings'] ?? [];
      $password = wp_generate_password();
      $primary_role = 'customer';
      $additional_roles = [];

      if (isset($import_data) && $import_data['setRole'] === true) {
        $mapped_roles = [];
        foreach ($group_ids as $group_id) {
          foreach ($role_mappings as $role => $mapping) {
            if (isset($mapping['groupId']) && $mapping['groupId'] === $group_id) {
              $mapped_roles[$role] = $mapping['priority'] ?? PHP_INT_MAX;
            }
          }
        }

        if (!empty($mapped_roles)) {
          asort($mapped_roles); // Sort roles by priority, ascending
          $primary_role = key($mapped_roles); // The role with the lowest priority
          $additional_roles = array_keys(array_slice($mapped_roles, 1)); // Any additional roles
        }
      }


      if (isset($import_data) && $import_data['emails'] === true) {
        add_filter('wp_new_user_notification_email', '__return_false');
        add_filter('send_password_change_email', '__return_false');
      }

      $user_id = wp_insert_user([
        'user_login' => $username,
        'user_email' => $email,
        'user_pass'  => $password,
        'role'       => $primary_role,
        'display_name' => $name,
        'first_name' => $customer['first_name'] ?? '',
        'last_name' => $customer['last_name'] ?? ''
      ]);

      if (isset($import_data) && $import_data['emails'] === true) {
        remove_filter('wp_new_user_notification_email', '__return_false');
        remove_filter('send_password_change_email', '__return_false');
      }

      if (is_wp_error($user_id)) {
        return new WP_Error('creating_user', 'Unable to create user with email: ' . $email);
      }

      update_user_meta($user_id, 'square_customer_id', $square_id);

      // Add additional roles
      $user = new \WP_User($user_id);
      foreach ($additional_roles as $additional_role) {
        $user->add_role($additional_role);
      }

      $wpdb->update(
        $wpdb->prefix . 'square_woo_customers',
        ['status' => 1, 'source' => 'Both'],
        ['square_customer_id' => $square_id]
      );

      return $name;
    } catch (\Exception $e) {
      error_log('Error in create_wordpress_user: ' . $e->getMessage());
      return new WP_Error('rest_create_wordpress_user', esc_html($e->getMessage()));
    }
  }



  public function match_customers(WP_REST_Request $request)
  {
    global $wpdb;

    try {
      $set_role    = $request->get_param('setrole') === 'true';
      $batch_size  = 100;
      $total_users = $wpdb->get_var("SELECT COUNT(ID) FROM {$wpdb->users}");

      if ($total_users === null) {
        throw new \Exception('Error retrieving the total number of WordPress users');
      }

      $settings      = get_option('square-woo-sync_settings', []);
      $role_mappings = $settings['customers']['roleMappings'] ?? [];

      for ($offset = 0; $offset < $total_users; $offset += $batch_size) {
        $wp_users = new \WP_User_Query([
          'number' => $batch_size,
          'offset' => $offset,
        ]);

        $user_results = $wp_users->get_results();
        if ($user_results === null) {
          throw new \Exception('Error retrieving WordPress users');
        }

        $user_emails = array_map(function ($user) {
          return strtolower($user->user_email);
        }, $user_results);

        if (!empty($user_emails)) {
          $placeholders = implode(',', array_fill(0, count($user_emails), '%s'));
          $sql = "
            SELECT id, LOWER(email) AS email, square_customer_id, group_ids
              FROM {$wpdb->prefix}square_woo_customers
             WHERE source = 'Square'
               AND status != 1
               AND LOWER(email) IN ($placeholders)
          ORDER BY LOWER(email) ASC, has_loyalty DESC, id ASC
          ";

          $square_customers = $wpdb->get_results(
            $wpdb->prepare($sql, ...$user_emails),
            ARRAY_A
          );
          if ($square_customers === null) {
            throw new \Exception('Error retrieving Square customers from the database');
          }

          // Build map, loyalty-first wins
          $square_customers_map = [];
          foreach ($square_customers as $cust) {
            $email = $cust['email'];
            if (!isset($square_customers_map[$email])) {
              $square_customers_map[$email] = [
                'square_customer_id' => $cust['square_customer_id'],
                'group_ids'          => $cust['group_ids'],
              ];
            }
          }

          // Match each WP user
          foreach ($user_results as $user) {
            $lowercase_email = strtolower($user->user_email);
            if (!isset($square_customers_map[$lowercase_email])) {
              continue;
            }

            $square_customer_id = sanitize_text_field(
              $square_customers_map[$lowercase_email]['square_customer_id']
            );
            $group_ids = array_map(
              'trim',
              explode(',', sanitize_text_field($square_customers_map[$lowercase_email]['group_ids']))
            );

            if (empty($square_customer_id) || $square_customer_id === 'n/a') {
              continue;
            }

            // Skip if already set
            $existing_meta = get_user_meta($user->ID, 'square_customer_id', true);
            if ($existing_meta === $square_customer_id) {
              continue;
            }

            // Update meta
            $result = update_user_meta($user->ID, 'square_customer_id', $square_customer_id);
            if ($result === false) {
              error_log("Failed to add/update user meta for user ID {$user->ID} with square_customer_id {$square_customer_id}");
              throw new \Exception("Failed to add/update user meta for user ID {$user->ID}");
            }

            // Ensure DB row status/source updated
            $wpdb->update(
              "{$wpdb->prefix}square_woo_customers",
              ['status' => 1, 'source' => 'Both'],
              ['email' => $lowercase_email]
            );

            // Remove any Woo-only duplicates
            $wpdb->query(
              $wpdb->prepare(
                "DELETE FROM {$wpdb->prefix}square_woo_customers WHERE email = %s AND source = 'Woo'",
                $lowercase_email
              )
            );

            // Optionally set roles
            if ($set_role) {
              $mapped_roles = [];
              foreach ($group_ids as $group_id) {
                foreach ($role_mappings as $role => $mapping) {
                  if (isset($mapping['groupId']) && $mapping['groupId'] === $group_id) {
                    $mapped_roles[$role] = $mapping['priority'] ?? PHP_INT_MAX;
                  }
                }
              }
              if (!empty($mapped_roles)) {
                asort($mapped_roles);
                $primary_role     = key($mapped_roles);
                $additional_roles = array_keys(array_slice($mapped_roles, 1));
                $user->set_role($primary_role);
                foreach ($additional_roles as $additional_role) {
                  $user->add_role($additional_role);
                }
              }
            }
          }
        }
      }

      return new WP_REST_Response([
        'success' => true,
        'message' => esc_html__('Customers matched successfully', 'squarewoosync-pro'),
      ], 200);
    } catch (\Exception $e) {
      error_log('Error in match_customers: ' . $e->getMessage());
      return new WP_Error(
        'rest_match_customers_error',
        esc_html($e->getMessage()),
        ['status' => 500]
      );
    }
  }



  public function get_customers(WP_REST_Request $request): WP_REST_Response
  {
    global $wpdb;
    try {
      $this->maybe_create_customers_table();


      $squareHelper = new SquareHelper();

      if ($squareHelper->get_active_token() !== null) {
        $force = $request->get_param('force') === 'true';
        $table_name = $wpdb->prefix . 'square_woo_customers';
        $cron_option = 'squarewoosync_get_customers';

        if ($force) {
          // Clear the table and set the cron status to running
          $result = $wpdb->query("TRUNCATE TABLE $table_name");
          if ($result === false) {
            throw new \Exception('Failed to truncate table.');
          }

          wp_schedule_single_event(time(), $cron_option);

          // Ensure settings exist
          $settings = get_option('square-woo-sync_settings', []);
          if (!isset($settings['customers'])) {
            $settings['customers'] = [];
          }
          $settings['customers']['isFetching'] = 1;

          $update_result = update_option('square-woo-sync_settings', $settings);

          if ($update_result === false) {
            throw new \Exception('Failed to update settings.');
          }

          return new WP_REST_Response(['message' => 'Fetching data, please wait...', 'loading' => true, 'data' => []], 200);
        } else {
          // Ensure settings exist
          $settings = get_option('square-woo-sync_settings', []);
          if (!isset($settings['customers'])) {
            $settings['customers'] = [];
          }
          $isRunning = isset($settings['customers']['isFetching']) ? $settings['customers']['isFetching'] : 0;

          if (wp_next_scheduled($cron_option) || $isRunning === 1) {
            return new WP_REST_Response(['message' => 'Data is being fetched, please wait...', 'loading' => true, 'data' => []], 200);
          }

          $per_page = $request->get_param('page_size') ?? 10;

          // 1. Get total count of records (no LIMIT)
          $total = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$table_name}");

          // 2. Query the first $per_page rows
          $results = $wpdb->get_results(
            $wpdb->prepare(
              "SELECT * FROM {$table_name} LIMIT %d",
              $per_page
            ),
            ARRAY_A
          );

          $filteredTotal = $total; // The total count from the database (assumes LIKE filtering is sufficient)
          $response = rest_ensure_response(['message' => 'Data has been fetched', 'loading' => false, 'data' => $results]);
          $response->header('X-WP-Total', $filteredTotal);
          $response->header('X-WP-TotalPages', ceil($filteredTotal / $per_page));


          return $response;
        }
      } else {
        error_log('Invalid Access Token');
        return new WP_REST_Response(['message' => 'Invalid Access Token', 'loading' => false], 500);
      }
    } catch (\Exception $e) {
      error_log('Error in get_customers: ' . $e->getMessage());
      return new WP_REST_Response(['message' => 'An error occurred: ' . $e->getMessage(), 'loading' => false, 'data' => []], 500);
    }
  }

  private function maybe_create_customers_table()
  {
    global $wpdb;

    // 1) Define the canonical schema, including the new has_loyalty column
    $table_name      = $wpdb->prefix . 'square_woo_customers';
    $charset_collate = $wpdb->get_charset_collate();

    $sql = "CREATE TABLE `{$table_name}` (
        id                   INT            NOT NULL AUTO_INCREMENT,
        first_name           VARCHAR(255)   DEFAULT '',
        last_name            VARCHAR(255)   DEFAULT '',
        email                VARCHAR(100)   NOT NULL,
        source               VARCHAR(50)    NOT NULL,
        group_ids            VARCHAR(100)   DEFAULT 'N/A',
        group_names          VARCHAR(255)   DEFAULT 'N/A',
        role                 VARCHAR(100)   DEFAULT 'N/A',
        status               BOOLEAN        DEFAULT 0,
        square_customer_id   VARCHAR(255),
        has_loyalty          TINYINT(1)     NOT NULL DEFAULT 0,
        PRIMARY KEY (id)
    ) {$charset_collate};";

    require_once ABSPATH . 'wp-admin/includes/upgrade.php';
    dbDelta($sql);

    // 2) Only if the table really exists, check for has_loyalty
    $table_exists = $wpdb->get_var($wpdb->prepare(
      "SHOW TABLES LIKE %s",
      $table_name
    ));

    if ($table_exists === $table_name) {

      $col = $wpdb->get_var($wpdb->prepare(
        "SHOW COLUMNS FROM `{$table_name}` LIKE %s",
        'has_loyalty'
      ));

      if (! $col) {
        $wpdb->query(
          "ALTER TABLE `{$table_name}` 
                 ADD COLUMN `has_loyalty` TINYINT(1) NOT NULL DEFAULT 0"
        );
      }
    }
  }


  public function schedule_customer_cron()
  {
    try {
      // Ensure settings exist
      $settings = get_option('square-woo-sync_settings', []);
      if (!isset($settings['customers'])) {
        $settings['customers'] = [];
      }
      $settings['customers']['isFetching'] = 1;
      update_option('square-woo-sync_settings', $settings);

      $this->fetch_and_store_square_customers();
      $this->process_wp_users();
      $this->bulk_loyalty_search();

      $settings['customers']['isFetching'] = 0;
      update_option('square-woo-sync_settings', $settings);
    } catch (\Exception $e) {
      error_log('Error in schedule_customer_cron: ' . $e->getMessage());

      // Ensure fetching status is reset on error
      $settings = get_option('square-woo-sync_settings', []);
      $settings['customers']['isFetching'] = 0;
      update_option('square-woo-sync_settings', $settings);
    }
  }


  public function fetch_and_store_square_customers(): void
  {
    global $wpdb;
    $squareHelper = new SquareHelper();
    $cursor = null;

    $settings = get_option('square-woo-sync_settings', []);

    try {
      $groups = $squareHelper->square_api_request('/customers/groups');
      $groups = $groups['data']['groups'] ?? [];

      $groupIdToNameMap = [];
      foreach ($groups as $group) {
        $groupIdToNameMap[$group['id']] = $group['name'];
      }

      do {
        $filter = [];

        if (!empty($settings['customers']['filters']['group']) && $settings['customers']['filters']['group'] !== "0") {
          $filter['group_ids'] = ['any' => [$settings['customers']['filters']['group']]];
        }

        if (!empty($settings['customers']['filters']['segment']) && $settings['customers']['filters']['segment'] !== "0") {
          $filter['segment_ids'] = ['any' => [$settings['customers']['filters']['segment']]];
        }

        $body = [];

        if (!empty($filter)) {
          $body['query'] = [
            'filter' => $filter
          ];
        }

        if ($cursor) {
          $body['cursor'] = $cursor;
        }


        $url = '/customers/search';

        $response = $squareHelper->square_api_request($url, 'POST', $body);

        if (!$response['success']) {
          throw new \Exception('Failed to fetch customers from Square');
        }

        if (isset($response['data']['customers']) && is_array($response['data']['customers'])) {
          foreach ($response['data']['customers'] as $customer) {
            if (empty($customer['email_address'])) {
              continue;
            }

            $has_loyalty = false;

            $group_ids = isset($customer['group_ids']) ? implode(', ', $customer['group_ids']) : '';
            $group_names = array_map(function ($id) use ($groupIdToNameMap) {
              return $groupIdToNameMap[$id] ?? '';
            }, explode(', ', $group_ids));

            $customer_data = [
              'first_name' => sanitize_text_field(($customer['given_name'] ?? '')),
              'last_name' => sanitize_text_field(($customer['family_name'] ?? '')),
              'email' => sanitize_email($customer['email_address']),
              'source' => 'Square',
              'group_ids' => sanitize_text_field($group_ids),
              'group_names' => sanitize_text_field(implode(', ', $group_names)),
              'role' => '',
              'status' => 0,
              'square_customer_id' => sanitize_text_field($customer['id']),
              'has_loyalty' => $has_loyalty ? '1' : '0'
            ];

            $existing_customer = $wpdb->get_var($wpdb->prepare("
                        SELECT id FROM {$wpdb->prefix}square_woo_customers
                        WHERE square_customer_id = %s
                    ", $customer['id']));

            if ($existing_customer) {
              $wpdb->update(
                $wpdb->prefix . 'square_woo_customers',
                $customer_data,
                ['id' => $existing_customer]
              );
            } else {
              $wpdb->insert(
                $wpdb->prefix . 'square_woo_customers',
                $customer_data
              );
            }
          }
        }

        $cursor = $response['data']['cursor'] ?? null;
      } while ($cursor);
    } catch (\Exception $e) {
      error_log('Error in fetch_and_store_square_customers: ' . $e->getMessage());
    }
  }

  private function bulk_loyalty_search()
  {
    global $wpdb;
    $table = $wpdb->prefix . 'square_woo_customers';
    $all_ids = $wpdb->get_col(
      "SELECT square_customer_id FROM $table WHERE square_customer_id IS NOT NULL"
    );

    if (empty($all_ids)) {
      // No customers at all
      return;
    }

    $squareHelper  = new SquareHelper();
    $hasLoyaltyMap = [];

    foreach (array_chunk($all_ids, 25) as $batch) {
      $body = [
        'query' => [
          'customer_ids' => $batch
        ],
      ];
      $resp = $squareHelper->square_api_request(
        '/loyalty/accounts/search',
        'POST',
        $body
      );

      if ($resp['success'] && ! empty($resp['data']['loyalty_accounts'])) {
        foreach ($resp['data']['loyalty_accounts'] as $acct) {
          $hasLoyaltyMap[$acct['customer_id']] = true;
        }
      }
    }

    // If none have loyalty, clear the flag en masse:
    if (empty($hasLoyaltyMap)) {
      $wpdb->query("UPDATE $table SET has_loyalty = 0");
      return;
    }

    // Build CASE statement:
    $cases = [];
    foreach (array_keys($hasLoyaltyMap) as $cust_id) {
      $cid = esc_sql($cust_id);
      $cases[] = "WHEN '$cid' THEN 1";
    }
    $case_sql = implode(' ', $cases);

    // One big UPDATE:
    $wpdb->query(
      "UPDATE {$table}
       SET has_loyalty = CASE square_customer_id
         {$case_sql}
         ELSE 0
       END"
    );
  }

  private function process_wp_users(): void
  {
    global $wpdb;

    try {
      $squareHelper = new SquareHelper();
      $groups = $squareHelper->square_api_request('/customers/groups');
      $groups = $groups['data']['groups'] ?? [];

      $groupIdToNameMap = [];
      foreach ($groups as $group) {
        $groupIdToNameMap[$group['id']] = $group['name'];
      }

      $wp_users = new \WP_User_Query([
        'number' => -1
      ]);

      $user_results = $wp_users->get_results();

      if (!empty($user_results)) {
        foreach ($user_results as $user) {
          $square_customer_id = get_user_meta($user->ID, 'square_customer_id', true);
          $square_customer = null;

          $billing_first_name = get_user_meta($user->ID, 'billing_first_name', true);
          $billing_last_name = get_user_meta($user->ID, 'billing_last_name', true);

          $first_name = sanitize_text_field($user->first_name ?: $billing_first_name);
          $last_name = sanitize_text_field($user->last_name ?: $billing_last_name);

          if ($square_customer_id) {
            $square_customer = $wpdb->get_row($wpdb->prepare("
                        SELECT * FROM {$wpdb->prefix}square_woo_customers
                        WHERE square_customer_id = %s
                    ", $square_customer_id), ARRAY_A);
          }

          if ($square_customer) {
            $group_ids = explode(', ', $square_customer['group_ids']);
            $group_names = array_map(function ($id) use ($groupIdToNameMap) {
              return $groupIdToNameMap[$id] ?? '';
            }, $group_ids);

            $customer_data = [
              'first_name' => $first_name ?: '',
              'last_name' => $last_name ?: '',
              'email' => sanitize_email($user->user_email ?: ''),
              'source' => 'Both',
              'group_ids' => sanitize_text_field($square_customer['group_ids']),
              'group_names' => sanitize_text_field(implode(', ', $group_names)),
              'role' => sanitize_text_field(implode(', ', $user->roles)),
              'status' => 1,
              'square_customer_id' => $square_customer_id
            ];

            $wpdb->update(
              $wpdb->prefix . 'square_woo_customers',
              $customer_data,
              ['id' => $square_customer['id']]
            );
          } else {
            $group_ids = '';
            $group_names = '';

            $user_group_ids = get_user_meta($user->ID, 'square_group_ids', true);
            if (!empty($user_group_ids)) {
              $group_ids = sanitize_text_field($user_group_ids);
              $group_names_array = array_map(function ($id) use ($groupIdToNameMap) {
                return $groupIdToNameMap[$id] ?? '';
              }, explode(', ', $group_ids));
              $group_names = sanitize_text_field(implode(', ', $group_names_array));
            }

            $customer_data = [
              'first_name' => $first_name ?: '',
              'last_name' => $last_name ?: '',
              'email' => sanitize_email($user->user_email ?: ''),
              'source' => 'Woo',
              'group_ids' => $group_ids,
              'group_names' => $group_names,
              'role' => sanitize_text_field(implode(', ', $user->roles)),
              'status' => 0,
              'square_customer_id' => sanitize_text_field($square_customer_id ?: '')
            ];

            $wpdb->insert(
              $wpdb->prefix . 'square_woo_customers',
              $customer_data
            );
          }
        }
      }
    } catch (\Exception $e) {
      error_log('Error in process_wp_users: ' . $e->getMessage());
    }
  }


  /**
   * Retrieves Square Customer Groups and Segments with transient caching and optional force refresh.
   *
   * @param WP_REST_Request $request
   * @return WP_REST_Response|WP_Error
   */
  public function get_groups_segments(WP_REST_Request $request): WP_REST_Response | WP_Error
  {
    $cache_key = 'sws_square_groups_segments';
    $force_refresh = $request->get_param('force');

    if (!$force_refresh && ($cached = get_transient($cache_key)) !== false) {
      return new WP_REST_Response($cached, 200);
    }

    try {
      $squareHelper = new SquareHelper();

      if ($squareHelper->get_active_token() === null) {
        return new WP_REST_Response('invalid access token', 401);
      }

      $groups = $squareHelper->square_api_request('/customers/groups');
      $segments = $squareHelper->square_api_request('/customers/segments');

      $groups = $groups['data']['groups'] ?? [];
      $segments = $segments['data']['segments'] ?? [];

      $response_data = [
        'groups' => $groups,
        'segments' => $segments,
        'cached' => false,
      ];

      set_transient($cache_key, $response_data, 600); // Cache for 10 minutes

      return new WP_REST_Response($response_data, 200);
    } catch (\Exception $e) {
      error_log('Error retrieving Square Customer Groups or Segments: ' . $e->getMessage());
      return new WP_Error('rest_square_error', esc_html($e->getMessage()), ['status' => 500]);
    }
  }


  public function get_role_mappings(WP_REST_Request $request): WP_REST_Response | WP_ERROR
  {
    try {
      $settings = get_option('square-woo-sync_settings', []);
      $roleMappings = $settings['customers']['roleMappings'] ?? [];

      return new WP_REST_Response(['roleMappings' => $roleMappings], 200);
    } catch (\Exception $e) {
      error_log('Error retrieving role mappings: ' . $e->getMessage());
      return new WP_Error('rest_square_error', esc_html($e->getMessage()), ['status' => 500]);
    }
  }

  public function set_role_mappings(WP_REST_Request $request)
  {
    $roleMappings = $request->get_param('roleMappings');

    if (is_array($roleMappings)) {
      $settings = get_option('square-woo-sync_settings', []);
      $settings['customers']['roleMappings'] = $roleMappings;
      update_option('square-woo-sync_settings', $settings);

      return new WP_REST_Response(['status' => 'success', 'roleMappings' => $roleMappings], 200);
    }

    return new WP_REST_Response(['status' => 'error', 'message' => 'Invalid role mappings data'], 400);
  }

  public function schedule_cron()
  {
    $this->fetch_and_store_square_customers();
    $this->process_wp_users();
    $this->bulk_loyalty_search();
  }

  public function get_groups(WP_REST_Request $request): WP_REST_Response
  {

    try {
      $squareHelper = new SquareHelper();

      if ($squareHelper->get_active_token() !== null) {
        $groups = $squareHelper->square_api_request('/customers/groups');
        $groups = $groups['data']['groups'] ?? [];

        global $wp_roles;
        if (!isset($wp_roles)) {
          $wp_roles = new \WP_Roles();
        }
        $roles = $wp_roles->get_names();

        $settings = get_option('square-woo-sync_settings', []);
        $roleMappings = $settings['customers']['roleMappings'] ?? [];

        $response_data = [
          'square_groups' => $groups,
          'wp_user_roles' => $roles,
          'roleMappings' => $roleMappings,
        ];

        return new WP_REST_Response($response_data, 200);
      } else {
        return new WP_REST_Response('invalid access token', 401);
      }
    } catch (\Exception $e) {
      error_log('Error retrieving Square Customer Groups or WordPress User Roles: ' . $e->getMessage());
      return new WP_REST_Response(esc_html($e->getMessage()), 500);
    }
  }


  /**
   * Fetches saved customer data from the database (limited to 10).
   *
   * @return WP_REST_Response|WP_Error
   */
  public function get_saved_customers(WP_REST_Request $request)
  {
    $settings = get_option('square-woo-sync_settings', []);

    // Check if the cron job is already running
    if ($settings['customers']['isFetching'] === 1) {
      return new WP_REST_Response(['message' => 'Customers loading', 'finished' => false], 200);
    }

    global $wpdb;
    $table_name = $wpdb->prefix . 'square_woo_customers';

    // We'll define per_page = 10 for this "first 10" scenario.
    $per_page = $request->get_param('page_size') ?? 10;

    // 1. Get total count of records (no LIMIT)
    $total = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$table_name}");

    // 2. Query the first $per_page rows
    $results = $wpdb->get_results(
      $wpdb->prepare(
        "SELECT * FROM {$table_name} LIMIT %d",
        $per_page
      ),
      ARRAY_A
    );

    // 4. Prepare REST response
    $response = rest_ensure_response(['customers' => $results, 'finished' => true]);

    // 5. Calculate total pages (in this fixed scenario, there's only one page shown in the response, 
    //    but let's still provide the headers for consistency)
    $total_pages = ceil($total / $per_page);

    $response->header('X-WP-Total', $total);
    $response->header('X-WP-TotalPages', $total_pages);

    return $response;
  }

  /**
   * Search customers with optional filters, pagination, and sorting.
   * 
   * GET Parameters:
   *  - search    (string) : free-text search, matches first_name, last_name, email, group_ids, group_names (case-insensitive)
   *  - page      (int)    : 1-based page index (default 1)
   *  - per_page  (int)    : items per page (default 20, max 100)
   *  - orderby   (string) : sorting field (allowed: id, first_name, last_name, email, role, status)
   *  - order     (string) : "asc" or "desc" (default asc)
   *  - source    (string) : "Square", "Woo", or "Both" - filter by customer source
   *  - status    (string) : "true"/"1" or "false"/"0" - filter by status column
   *  - role      (string) : filter by role
   *  - group     (string) : filter by group, matches group_ids
   */
  public function search_customers(WP_REST_Request $request)
  {
    global $wpdb;
    $table_name = $wpdb->prefix . 'square_woo_customers';

    try {
      // 1) Gather and sanitize input
      $search   = trim($request->get_param('search'));
      $page     = max(1, (int) $request->get_param('page', 1));
      $per_page = max(1, min(100, (int) $request->get_param('per_page', 20)));
      $offset   = ($page - 1) * $per_page;

      // Sorting
      $sortBy = sanitize_text_field($request->get_param('orderby'));
      $sortOrder = strtoupper(sanitize_text_field($request->get_param('order')));
      $sortOrder = ($sortOrder === 'DESC') ? 'DESC' : 'ASC';

      // Additional filters
      $sourceParam = trim($request->get_param('source'));
      $statusParam = trim($request->get_param('status'));
      $roleParam   = trim($request->get_param('role'));
      $groupParam  = trim($request->get_param('group'));
      $loyaltyParam  = trim($request->get_param('has_loyalty'));

      // 2) Allowed sort fields (column => real DB column)
      $allowedSort = [
        'id'         => 'id',
        'first_name' => 'first_name',
        'last_name'  => 'last_name',
        'email'      => 'email',
        'role'       => 'role',
        'status'     => 'status', // boolean
        'source'     => 'source'
      ];

      // Build ORDER BY clause
      $orderByClause = '';
      if ($sortBy && array_key_exists($sortBy, $allowedSort)) {
        $orderByClause = "ORDER BY {$allowedSort[$sortBy]} {$sortOrder}";
      }

      // 3) Build WHERE clauses
      $clauses = [];
      $where_params = [];

      // search: match first_name, last_name, email, group_ids, group_names
      if ($search !== '') {
        $search_like = '%' . $wpdb->esc_like(strtolower($search)) . '%';
        $clauses[] = "(
                LOWER(first_name) LIKE %s OR 
                LOWER(last_name)  LIKE %s OR
                LOWER(email)      LIKE %s OR
                LOWER(group_ids)  LIKE %s OR
                LOWER(group_names) LIKE %s
            )";
        array_push($where_params, $search_like, $search_like, $search_like, $search_like, $search_like);
      }

      // Filter by source, e.g. 'Square', 'Woo', 'Both'
      if ($sourceParam !== '') {
        $clauses[] = "LOWER(source) = %s";
        $where_params[] = strtolower($sourceParam);
      }

      // Filter by role
      if ($roleParam !== '') {
        // If you want exact match: "role = %s"
        // Or partial match if a user can have multiple roles stored in that column
        $clauses[] = "LOWER(role) LIKE %s";
        $where_params[] = '%' . $wpdb->esc_like(strtolower($roleParam)) . '%';
      }

      // Filter by status (boolean 0/1)
      if ($statusParam !== '') {
        // "true"/"1" => status=1, "false"/"0" => status=0
        if (in_array(strtolower($statusParam), ['1', 'true'], true)) {
          $clauses[] = "status = 1";
        } else if (in_array(strtolower($statusParam), ['0', 'false'], true)) {
          $clauses[] = "status = 0";
        }
      }

      // Filter by group in group_ids
      if ($groupParam !== '') {
        // A simple approach is searching the groupParam as a substring in group_ids
        // e.g. "FIND_IN_SET" if group_ids are comma-separated IDs:
        // $clauses[] = "FIND_IN_SET(%s, group_ids) <> 0";
        // $where_params[] = $groupParam;

        // Or partial match (in case group_ids is a string)
        $clauses[] = "LOWER(group_ids) LIKE %s";
        $where_params[] = '%' . $wpdb->esc_like(strtolower($groupParam)) . '%';
      }

      if ($loyaltyParam !== '') {
        $clauses[] = in_array(strtolower($loyaltyParam), ['1', 'true', 'yes'], true)
          ? 'has_loyalty = 1'
          : 'has_loyalty = 0';
      }

      // Combine where
      $where_sql = '';
      if (!empty($clauses)) {
        $where_sql = 'WHERE ' . implode(' AND ', $clauses);
      }

      // 4) Count total
      $count_sql = "SELECT COUNT(*) FROM {$table_name} {$where_sql}";
      $total = (int) $wpdb->get_var($wpdb->prepare($count_sql, $where_params));

      // 5) Fetch the data
      $data_sql = "SELECT * FROM {$table_name} {$where_sql} {$orderByClause} LIMIT %d OFFSET %d";
      $all_params = array_merge($where_params, [$per_page, $offset]);
      $results = $wpdb->get_results($wpdb->prepare($data_sql, $all_params), ARRAY_A);

      // 6) Return paginated response
      $response = rest_ensure_response($results);
      $response->header('X-WP-Total', $total);
      $response->header('X-WP-TotalPages', ceil($total / $per_page));
      return $response;
    } catch (\Exception $e) {
      return new WP_Error(
        'search_customers_error',
        'An error occurred while searching customers: ' . $e->getMessage(),
        ['status' => 500]
      );
    }
  }
}
