<?php

/**
 * SSO Group Provisioning Functions
 * 
 * Functions for handling group-based SSO configuration (shared vs individual accounts)
 * 
 * @package SSO
 */

// require_once(dirname(__FILE__) . '/sso-utilities.php');
// require_once(dirname(__FILE__) . '/sso-user-provisioning.php');
// require_once(dirname(__FILE__) . '/sso-aad-client.php');
// require_once(dirname(__FILE__) . '/sso-storage.php');
// require_once(dirname(__FILE__) . '/../sso-tenant-manager.php');

function save_groups_to_XI()
{
    check_nagios_session_protector();

    $users_raw = grab_request_var('users', '[]');
    $usersFormData = json_decode($users_raw, true);

    if (!is_array($usersFormData) || empty($usersFormData)) {
        log_sso('save_groups_to_XI: No group configuration data provided. usersFormData is: ' . var_export($usersFormData, true), 'error');
        echo json_encode(array('status' => 'error', 'message' => 'No group configuration data provided.'));
        exit();
    }

    $processed_groups = 0;
    $errors = array();
    $skipped_count = 0;
    $total_entries = count($usersFormData);
    $all_statistics = array();

    try {
        foreach ($usersFormData as $key => $config) {
            if (!is_array($config)) {
                $skipped_count++;
                continue;
            }

            // Skip individual user entries (they should use save_users_to_XI)
            if (array_key_exists('user_id_aad', $config) && !array_key_exists('account_mode', $config)) {
                $skipped_count++;
                continue;
            }

            // Determine if this is a group configuration
            $account_mode = grab_array_var($config, 'account_mode');
            $group_ids = array();

            // Always check for group_ids in config first (preferred method for unified format)
            // This allows the frontend to send a single unified configuration with group_ids array
            $group_ids = grab_array_var($config, 'group_ids', array());
            
            if (empty($group_ids)) {
                // Fallback: Check if key is a group ID (for backward compatibility)
                if ($key !== 'individual_template' && !empty($key)) {
                    // For shared mode, the key might be the group ID
                    if ($account_mode === 'shared') {
                        $group_ids[] = $key;
                    } else if ($account_mode === 'individual') {
                        // For individual mode, try using key as group ID if no group_ids in config
                        if ($key !== 'individual_template') {
                            $group_ids[] = $key;
                        }
                    } else {
                        log_sso('save_groups_to_XI: Entry key "' . $key . '" - account_mode is neither "shared" nor "individual": ' . var_export($account_mode, true), 'warning');
                    }
                }
            }

            // Additional fallback for individual_template mode - check request parameter
            if (empty($group_ids) && $account_mode === 'individual' && $key === 'individual_template') {
                $group_ids_raw = grab_request_var('group_ids', array());
                if (is_string($group_ids_raw)) {
                    $group_ids = json_decode($group_ids_raw, true);
                } else {
                    $group_ids = $group_ids_raw;
                }
            }

            if (empty($group_ids)) {
                log_sso('save_groups_to_XI: Skipping entry with key "' . $key . '" - No group IDs found. account_mode: ' . var_export($account_mode, true) . ', key: ' . var_export($key, true), 'warning');
                $skipped_count++;
                continue;
            }

            $client_id = grab_array_var($config, 'client_id', '');
            if (empty($client_id)) {
                $error_msg = 'Missing client_id for group configuration: ' . $key;
                $errors[] = $error_msg;
                $skipped_count++;
                continue;
            }

            $tenant_details = get_tenants_credentials_helper($client_id);
            $tenant_id = grab_array_var($tenant_details, 'tenant_id', '');
            if (empty($tenant_id)) {
                $error_msg = 'Failed to determine tenant ID for client: ' . $client_id;
                log_sso('save_groups_to_XI: ' . $error_msg . ', tenant_details: ' . var_export($tenant_details, true), 'error');
                $errors[] = $error_msg;
                $skipped_count++;
                continue;
            }

            // Process group configuration
            try {
                $result = process_group_sso_configuration($group_ids, $account_mode, $config, $client_id, $tenant_id);
                $warnings = $result['warnings'];
                $group_statistics = $result['statistics'];
                
                if (!empty($warnings)) {
                    foreach ($warnings as $warning) {
                        $errors[] = $warning;
                    }
                }
                
                // Store statistics for aggregation
                if (!empty($group_statistics)) {
                    $all_statistics[] = array(
                        'group_ids' => $group_ids,
                        'account_mode' => $account_mode,
                        'statistics' => $group_statistics
                    );
                }
                
                $processed_groups++;
                log_sso('save_groups_to_XI: Entry key "' . $key . '" - Successfully processed. Total processed: ' . $processed_groups, 'info');
            } catch (Exception $e) {
                $error_msg = 'Error processing group configuration for ' . $key . ': ' . $e->getMessage();
                log_sso('save_groups_to_XI: ' . $error_msg . ', trace: ' . $e->getTraceAsString(), 'error');
                $errors[] = $error_msg;
            }
        }
    } catch (Exception $e) {
        log_sso('save_groups_to_XI: Exception in main loop: ' . $e->getMessage() . ', trace: ' . $e->getTraceAsString(), 'error');
        echo json_encode(array('status' => 'error', 'message' => 'Failed to save groups to Nagios XI: ' . $e->getMessage()));
        exit();
    }

    log_sso('save_groups_to_XI: Processing complete. Total entries: ' . $total_entries . ', Processed: ' . $processed_groups . ', Skipped: ' . $skipped_count . ', Errors: ' . count($errors), 'info');

    if ($processed_groups === 0 && !empty($errors)) {
        log_sso('save_groups_to_XI: No groups processed, returning error. Errors: ' . implode('; ', $errors), 'error');
        echo json_encode(array('status' => 'error', 'message' => 'Failed to process any groups: ' . implode('; ', $errors)));
        exit();
    }

    // Aggregate statistics from all processed groups
    $aggregated_statistics = array(
        'users_created' => 0,
        'users_linked' => 0,
        'users_linked_existing' => 0,
        'users_skipped_no_email' => array(),
        'users_skipped_duplicate' => array(),
        'users_skipped_other' => array(),
        'shared_accounts_created' => 0,
        'total_groups_processed' => $processed_groups
    );
    
    if (!empty($all_statistics)) {
        foreach ($all_statistics as $group_stat) {
            $stats = $group_stat['statistics'];
            if (!empty($stats)) {
                if (isset($stats['users_created'])) {
                    $aggregated_statistics['users_created'] += $stats['users_created'];
                }
                if (isset($stats['users_linked'])) {
                    $aggregated_statistics['users_linked'] += $stats['users_linked'];
                }
                if (isset($stats['users_linked_existing'])) {
                    $aggregated_statistics['users_linked_existing'] += $stats['users_linked_existing'];
                }
                if (isset($stats['users_skipped_no_email']) && is_array($stats['users_skipped_no_email'])) {
                    $aggregated_statistics['users_skipped_no_email'] = array_merge($aggregated_statistics['users_skipped_no_email'], $stats['users_skipped_no_email']);
                }
                if (isset($stats['users_skipped_duplicate']) && is_array($stats['users_skipped_duplicate'])) {
                    $aggregated_statistics['users_skipped_duplicate'] = array_merge($aggregated_statistics['users_skipped_duplicate'], $stats['users_skipped_duplicate']);
                }
                if (isset($stats['users_skipped_other']) && is_array($stats['users_skipped_other'])) {
                    $aggregated_statistics['users_skipped_other'] = array_merge($aggregated_statistics['users_skipped_other'], $stats['users_skipped_other']);
                }
                if (isset($stats['shared_account_created']) && $stats['shared_account_created']) {
                    $aggregated_statistics['shared_accounts_created']++;
                }
            }
        }
    }
    
    // Build human-readable message
    $message = 'Successfully processed ' . $processed_groups . ' group configuration(s).';
    $warnings = array();
    $fatal_errors = array();
    
    // Separate warnings from fatal errors
    foreach ($errors as $error) {
        if (stripos($error, 'warning') !== false || stripos($error, 'skipped') !== false) {
            $warnings[] = $error;
        } else {
            $fatal_errors[] = $error;
        }
    }
    
    if (!empty($fatal_errors)) {
        $message .= ' Errors: ' . implode('; ', $fatal_errors);
    }
    
    if (!empty($warnings)) {
        $message .= ' Warnings: ' . implode('; ', $warnings);
    }

    // Comprehensive logging
    log_sso('save_groups_to_XI: Final Summary - Groups processed: ' . $aggregated_statistics['total_groups_processed'] . 
                  ', Users created: ' . $aggregated_statistics['users_created'] . 
                  ', Users linked: ' . $aggregated_statistics['users_linked'] . 
                  ', Users linked (existing): ' . $aggregated_statistics['users_linked_existing'] . 
                  ', Users skipped (no email): ' . count($aggregated_statistics['users_skipped_no_email']) . 
                  ', Users skipped (other): ' . count($aggregated_statistics['users_skipped_other']) . 
                  ', Shared accounts created: ' . $aggregated_statistics['shared_accounts_created'], 'info');
    
    // Log all skipped users
    if (!empty($aggregated_statistics['users_skipped_no_email'])) {
        log_sso('save_groups_to_XI: Users skipped due to missing email (' . count($aggregated_statistics['users_skipped_no_email']) . ' total):', 'warning');
        foreach ($aggregated_statistics['users_skipped_no_email'] as $skipped_user) {
            log_sso('save_groups_to_XI:   - "' . $skipped_user['display_name'] . '" (ID: ' . $skipped_user['object_id'] . ')', 'warning');
        }
    }
    if (!empty($aggregated_statistics['users_skipped_other'])) {
        log_sso('save_groups_to_XI: Users skipped due to other errors (' . count($aggregated_statistics['users_skipped_other']) . ' total):', 'warning');
        foreach ($aggregated_statistics['users_skipped_other'] as $skipped_user) {
            log_sso('save_groups_to_XI:   - "' . $skipped_user['display_name'] . '" (ID: ' . $skipped_user['object_id'] . ') - Reason: ' . $skipped_user['reason'], 'warning');
        }
    }

    log_sso('save_groups_to_XI: Returning success. Message: ' . $message, 'info');
    $response = array('status' => 'success', 'message' => $message, 'statistics' => $aggregated_statistics);
    if (!empty($warnings)) {
        $response['warnings'] = $warnings;
    }
    echo json_encode($response);
    exit();
}

function process_group_sso_configuration($group_ids, $account_mode, $config, $client_id, $tenant_id)
{
    if (empty($group_ids) || !is_array($group_ids)) {
        throw new Exception('Invalid group IDs provided');
    }

    if (!in_array($account_mode, array('individual', 'shared'))) {
        throw new Exception('Invalid account mode: ' . $account_mode);
    }

    // Extract form_data for user configuration fields
    $form_data = grab_array_var($config, 'form_data', array());

    // Check if separate_users flag is set (for shared mode with multiple groups)
    $separate_users = normalize_boolean_flag(grab_array_var($config, 'separate_users', 0));
    $group_names = grab_array_var($config, 'group_names', array()); // Map of group_id -> group_name
    
    // Get tenant name for template processing
    $tenant_details = get_tenants_credentials_helper($client_id);
    $tenant_name = grab_array_var($tenant_details, 'name', 'SSO');

    // For shared mode, create/get the shared XI user once before processing groups
    // This ensures all groups share the same account (unless separate_users is true)
    $shared_xi_user_id = null;
    $shared_user_was_new = false; // Track if the shared user was newly created
    if ($account_mode === 'shared' && !$separate_users) {
        $target_user_id = grab_array_var($config, 'user_id', 'none');
        
        if (!empty($target_user_id) && $target_user_id !== 'none') {
            // Use existing user specified in config
            $existing_users = get_users();
            $user_ids = array_map('intval', array_column($existing_users, 'user_id'));
            if (in_array((int)$target_user_id, $user_ids, true)) {
                $shared_xi_user_id = (int)$target_user_id;
                $shared_user_was_new = false; // User already existed
                log_sso('process_group_sso_configuration: Using existing shared user ID: ' . $shared_xi_user_id, 'info');
            }
        }
        
        if (!$shared_xi_user_id) {
            // Create new shared account user (will be reused for all groups)
            $name = grab_array_var($form_data, 'name', '');
            if (empty($name)) {
                // Check top-level namingTemplate first (for shared mode), then form_data
                $name = grab_array_var($config, 'namingTemplate', '');
                if (empty($name)) {
                    $name = grab_array_var($form_data, 'namingTemplate', 'Shared SSO Account');
                }
            }
            $name = sanitize_username_for_nagios($name, 'Shared SSO Account');

            // Check if user with this username already exists before creating
            $existing_user_id = get_user_id($name);
            $user_was_existing = false;
            if ($existing_user_id) {
                // User already exists - use existing user
                $shared_xi_user_id = $existing_user_id;
                $user_was_existing = true;
                $shared_user_was_new = false; // User already existed
                log_sso('process_group_sso_configuration: Shared account user "' . $name . '" already exists with username "' . $name . '", using existing user ID ' . $shared_xi_user_id, 'info');
            } else {
                // User doesn't exist - create new one
                $email = grab_array_var($form_data, 'email', '');
                $level = (strcasecmp(grab_array_var($form_data, 'auth_level', 'User'), 'Admin') === 0) ? 255 : 1;
                $password = bin2hex(random_bytes(16));
                $add_contact = normalize_boolean_flag(grab_array_var($form_data, 'add_contact', 0));
                $api_enabled = normalize_boolean_flag(grab_array_var($form_data, 'api_enabled', 0));
                $errmsg = array();

                $shared_xi_user_id = add_user_account($name, $password, $name, $email, $level, 0, $add_contact, $api_enabled, $errmsg);

                if (!$shared_xi_user_id || !empty($errmsg)) {
                    // Check if user was created despite error (duplicate username case)
                    $existing_user_id = get_user_id($name);
                    if ($existing_user_id) {
                        // User exists - use existing user
                        $shared_xi_user_id = $existing_user_id;
                        $user_was_existing = true;
                        $shared_user_was_new = false; // User already existed
                        log_sso('process_group_sso_configuration: Shared account user "' . $name . '" already exists with username "' . $name . '", using existing user ID ' . $shared_xi_user_id, 'info');
                    } else {
                        // User doesn't exist and creation failed
                        throw new Exception('Failed to create shared account user: ' . implode(', ', $errmsg));
                    }
                } else {
                    $shared_user_was_new = true; // User was newly created
                    log_sso('process_group_sso_configuration: Created shared account user - Name: "' . $name . '", ID: ' . $shared_xi_user_id, 'info');
                }

                // Set user meta and attributes only for newly created users
                if (!$user_was_existing) {
                    $user_meta = array(
                        'name' => $name,
                        'mobile_number' => grab_array_var($form_data, 'phone'),
                        'enable_notifications' => normalize_boolean_flag(grab_array_var($form_data, 'enable_notifications', 0)),
                        'language' => grab_array_var($form_data, 'language'),
                        'date_format' => grab_array_var($form_data, 'date_format'),
                        'number_format' => grab_array_var($form_data, 'number_format'),
                        'week_format' => grab_array_var($form_data, 'week_format'),
                        'userlevel' => $level,
                        'authorized_for_all_objects' => normalize_boolean_flag(grab_array_var($form_data, 'see_hosts_services', 0)),
                        'authorized_for_all_object_commands' => normalize_boolean_flag(grab_array_var($form_data, 'control_hosts_services', 0)),
                        'authorized_to_configure_objects' => normalize_boolean_flag(grab_array_var($form_data, 'configure_hosts_services', 0)),
                        'advanced_user' => normalize_boolean_flag(grab_array_var($form_data, 'advanced_user', 0)),
                        'authorized_for_monitoring_system' => normalize_boolean_flag(grab_array_var($form_data, 'monitoring_engine', 0)),
                        'readonly_user' => normalize_boolean_flag(grab_array_var($form_data, 'read_only', 0)),
                        'api_enabled' => normalize_boolean_flag(grab_array_var($form_data, 'api_enabled', 0)),
                        'autodeploy_access' => normalize_boolean_flag(grab_array_var($form_data, 'autodeploy_access', 0)),
                        'nagios_mod_gearman_access' => normalize_boolean_flag(grab_array_var($form_data, 'nagios_mod_gearman_access', 0)),
                        'ccm_access' => grab_array_var($form_data, 'core_config_manager_access', 0),
                        'tours' => serialize(array('new_user' => 1)),
                    );

                    $user_attributes = array(
                        'user_id' => $shared_xi_user_id,
                        'enabled' => normalize_boolean_flag(grab_array_var($form_data, 'enable_account', 1)),
                    );

                    set_new_user_meta_helper($user_meta, $user_attributes);
                }
            }
        }
    }

    // Process EACH group separately
    $warnings = array();
    $all_statistics = array();
    $total_users_processed = 0;
    
    // Hash set to track unique AAD user IDs across all groups (Not actually a hash set, but as close as we can get with PHP)
    $unique_aad_user_ids = array(); // Use array keys as hash set for O(1) lookup

    foreach ($group_ids as $group_id) {
        try {
            log_sso('process_group_sso_configuration: Processing group ' . $group_id, 'info');
            
            // Fetch users for THIS group only
            $group_users = fetch_group_members_paginated($group_id, $client_id);
            $group_user_count = count($group_users);
            $total_users_processed += $group_user_count;
            
            log_sso('process_group_sso_configuration: Group ' . $group_id . ' has ' . $group_user_count . ' members', 'info');
            
            $group_statistics = null;
            $xi_user_id = null;

            if ($account_mode === 'shared') {
                if ($separate_users) {
                    // Separate shared users mode: each group gets its own shared account
                    // Get group name from provided map or use group ID as fallback
                    $group_name = isset($group_names[$group_id]) ? $group_names[$group_id] : $group_id;
                    
                    // Generate unique name using template
                    // Check top-level namingTemplate first (for shared mode), then form_data
                    $naming_template = grab_array_var($config, 'namingTemplate', '');
                    if (empty($naming_template)) {
                        $naming_template = grab_array_var($form_data, 'namingTemplate', '<tenant_name:10> - <group_name>');
                    }
                    
                    // Prepare user data for template (no user fields for shared accounts)
                    $user_data = array(
                        'name' => '',
                        'displayName' => '',
                        'email' => '',
                        'givenName' => '',
                        'surname' => '',
                    );
                    
                    // Process template to generate unique name for this group
                    $generated_name = process_naming_template($naming_template, $user_data, $tenant_name, $group_name);
                    
                    // If template processing failed or returned empty, fallback to simple name
                    if (empty($generated_name)) {
                        $generated_name = $tenant_name . ' - ' . $group_name;
                    }
                    $generated_name = sanitize_username_for_nagios($generated_name, $tenant_name . ' - Group');
                    
                    // Clone config and set the generated name
                    $group_config = $config;
                    $group_config['name'] = $generated_name;
                    
                    // Process this group with its own shared account (pass null for pre-created user)
                    $result = process_shared_account_group($group_id, $client_id, $tenant_id, $group_config, $group_users, null);
                    $xi_user_id = isset($result['xi_user_id']) ? $result['xi_user_id'] : null;
                    
                    log_sso('process_group_sso_configuration: Created separate shared account for group ' . $group_id . ' - Name: "' . $generated_name . '"', 'info');
                } else {
                    // Shared account mode: all groups share the same XI user
                    // Pass the pre-created shared user ID and whether it was newly created
                    $result = process_shared_account_group($group_id, $client_id, $tenant_id, $config, $group_users, $shared_xi_user_id, $shared_user_was_new);
                    $xi_user_id = $shared_xi_user_id; // All groups use the same shared user
                }
                
                if (!empty($result['warnings'])) {
                    $warnings = array_merge($warnings, $result['warnings']);
                }
                if (!empty($result['statistics'])) {
                    $group_statistics = $result['statistics'];
                }
            } else {
                // Individual account mode: each group member gets their own XI user
                // Pass single group ID array for this group
                $result = process_individual_account_group(array($group_id), $client_id, $tenant_id, $config, $group_users);
                $xi_user_id = null;
                if (!empty($result['warnings'])) {
                    $warnings = array_merge($warnings, $result['warnings']);
                }
                if (!empty($result['statistics'])) {
                    $group_statistics = $result['statistics'];
                }
            }

            // Store group configuration metadata for THIS group
            save_group_config_metadata($group_id, $client_id, $account_mode, $xi_user_id, $config, $group_user_count);
            
            // Track users in hash set with user IDs from this group (Not actually a hash set, but as close as we can get with PHP)
            if (!empty($group_statistics)) {
                // Add user IDs from users_linked_ids (shared account mode)
                if (isset($group_statistics['users_linked_ids']) && is_array($group_statistics['users_linked_ids'])) {
                    foreach ($group_statistics['users_linked_ids'] as $user_id) {
                        if (!empty($user_id)) {
                            $unique_aad_user_ids[$user_id] = true;
                        }
                    }
                }
                // Add user IDs from users_created_ids (individual account mode)
                if (isset($group_statistics['users_created_ids']) && is_array($group_statistics['users_created_ids'])) {
                    foreach ($group_statistics['users_created_ids'] as $user_id) {
                        if (!empty($user_id)) {
                            $unique_aad_user_ids[$user_id] = true;
                        }
                    }
                }
                // Add user IDs from users_linked_existing_ids (individual account mode - existing users)
                if (isset($group_statistics['users_linked_existing_ids']) && is_array($group_statistics['users_linked_existing_ids'])) {
                    foreach ($group_statistics['users_linked_existing_ids'] as $user_id) {
                        if (!empty($user_id)) {
                            $unique_aad_user_ids[$user_id] = true;
                        }
                    }
                }
                
                $all_statistics[] = array(
                    'group_id' => $group_id,
                    'statistics' => $group_statistics
                );
            }
        } catch (Exception $e) {
            $error_msg = 'Error processing group ' . $group_id . ': ' . $e->getMessage();
            log_sso('process_group_sso_configuration: ' . $error_msg, 'error');
            $warnings[] = $error_msg;
            // Continue processing other groups even if one fails
        }
    }

    // Aggregate statistics from all groups into a single response
    $aggregated_statistics = null;
    if (!empty($all_statistics)) {
        $aggregated_statistics = array(
            'users_created' => 0,
            'users_linked' => 0,
            'users_linked_existing' => 0,
            'users_skipped_no_email' => array(),
            'users_skipped_other' => array(),
            'shared_accounts_created' => 0,
        );
        
        foreach ($all_statistics as $group_stats_entry) {
            $stats = $group_stats_entry['statistics'];
            
            if (isset($stats['users_created'])) {
                $aggregated_statistics['users_created'] += $stats['users_created'];
            }
            if (isset($stats['users_linked'])) {
                $aggregated_statistics['users_linked'] += $stats['users_linked'];
            }
            if (isset($stats['users_linked_existing'])) {
                $aggregated_statistics['users_linked_existing'] += $stats['users_linked_existing'];
            }
            if (isset($stats['users_skipped_no_email']) && is_array($stats['users_skipped_no_email'])) {
                $aggregated_statistics['users_skipped_no_email'] = array_merge(
                    $aggregated_statistics['users_skipped_no_email'],
                    $stats['users_skipped_no_email']
                );
            }
            if (isset($stats['users_skipped_other']) && is_array($stats['users_skipped_other'])) {
                $aggregated_statistics['users_skipped_other'] = array_merge(
                    $aggregated_statistics['users_skipped_other'],
                    $stats['users_skipped_other']
                );
            }
        }
        
        // Calculate additional metrics
        // Total number of configuration links (old way - sum of all links across groups)
        $aggregated_statistics['total_config_links'] = 
            $aggregated_statistics['users_created'] + 
            $aggregated_statistics['users_linked'] + 
            $aggregated_statistics['users_linked_existing'];
        
        // Number of unique AAD user IDs across all groups
        $aggregated_statistics['unique_aad_users'] = count($unique_aad_user_ids);
        
        // Number of XI users created (individual accounts + shared accounts)
            $aggregated_statistics['xi_users_created'] = 
            $aggregated_statistics['users_created'] + 
            $aggregated_statistics['shared_accounts_created'];
    }
    
    log_sso('process_group_sso_configuration: Completed processing ' . count($group_ids) . ' group(s), total config links: ' . (isset($aggregated_statistics['total_config_links']) ? $aggregated_statistics['total_config_links'] : 0) . ', unique AAD users: ' . count($unique_aad_user_ids), 'info');
    
    return array('warnings' => $warnings, 'statistics' => $aggregated_statistics);
}

function fetch_group_members_paginated($group_id, $client_id)
{
    $credentials = get_oauth_credentials($client_id);
    if (!$credentials) {
        throw new Exception('OAuth credentials not found');
    }

    $graph_client = create_graph_client($credentials);
    $all_members = array();
    $next_link = null;
    $page_size = 100;

    do {
        try {
            if ($next_link) {
                $response = $graph_client->get($next_link);
            } else {
                $headers = array('ConsistencyLevel' => 'eventual');
                $path = "v1.0/groups/{$group_id}/members?\$select=id,displayName,mail,userPrincipalName,givenName,surname,jobTitle,department,officeLocation&\$top={$page_size}";
                $response = $graph_client->get($path, array('headers' => $headers));
            }

            $data = json_decode($response->getBody(), true);
            if (isset($data['value'])) {
                $all_members = array_merge($all_members, $data['value']);
            }

            $next_link = grab_array_var($data, '@odata.nextLink');
        } catch (Exception $e) {
            log_sso('Error fetching group members (paginated): ' . $e->getMessage(), 'error');
            throw $e;
        }
    } while ($next_link);

    return $all_members;
}

function process_shared_account_group($group_id, $client_id, $tenant_id, $config, $users, $pre_created_xi_user_id = null, $pre_created_user_was_new = false)
{
    $warnings = array();
    
    // Extract form_data for user configuration fields
    $form_data = grab_array_var($config, 'form_data', array());
    
    // Structured statistics
    $statistics = array(
        'users_linked' => 0,
        'users_skipped_other' => array(),
        'shared_account_created' => false
    );
    
    // Get or create the shared XI user
    $xi_user_id = $pre_created_xi_user_id; // Use pre-created user if provided
    // If pre-created user was provided and it was newly created, mark it as such
    $shared_account_was_created = ($pre_created_xi_user_id !== null) ? $pre_created_user_was_new : false;
    
    // Update statistics if pre-created user was newly created
    if ($pre_created_user_was_new) {
        $statistics['shared_account_created'] = true;
    }

    if (!$xi_user_id) {
        // No pre-created user provided, check config for existing user or create new one
        $target_user_id = grab_array_var($config, 'user_id', 'none');

        if (!empty($target_user_id) && $target_user_id !== 'none') {
            $existing_users = get_users();
            $user_ids = array_map('intval', array_column($existing_users, 'user_id'));
            if (in_array((int)$target_user_id, $user_ids, true)) {
                $xi_user_id = (int)$target_user_id;
                // User already exists, not created via SSO
                $shared_account_was_created = false;
            }
        }

        if (!$xi_user_id) {
            // Create new shared account user
            $name = grab_array_var($form_data, 'name', '');
            if (empty($name)) {
                // Check top-level namingTemplate first (for shared mode), then form_data
                $name = grab_array_var($config, 'namingTemplate', '');
                if (empty($name)) {
                    $name = grab_array_var($form_data, 'namingTemplate', 'Shared SSO Account');
                }
            }
            $name = sanitize_username_for_nagios($name, 'Shared SSO Account');

            // Check if user with this username already exists before creating
            $existing_user_id = get_user_id($name);
            if ($existing_user_id) {
                // User already exists - use existing user
                $xi_user_id = $existing_user_id;
                $shared_account_was_created = false;
                log_sso('process_shared_account_group: Shared account user "' . $name . '" already exists with username "' . $name . '", using existing user ID ' . $xi_user_id, 'info');
            } else {
                // User doesn't exist - create new one
                $shared_account_was_created = true;
                $statistics['shared_account_created'] = true;
                $email = grab_array_var($form_data, 'email', '');
                $level = (strcasecmp(grab_array_var($form_data, 'auth_level', 'User'), 'Admin') === 0) ? 255 : 1;
                $password = bin2hex(random_bytes(16));
                $add_contact = normalize_boolean_flag(grab_array_var($form_data, 'add_contact', 0));
                $api_enabled = normalize_boolean_flag(grab_array_var($form_data, 'api_enabled', 0));
                $errmsg = array();

                $xi_user_id = add_user_account($name, $password, $name, $email, $level, 0, $add_contact, $api_enabled, $errmsg);

                if (!$xi_user_id || !empty($errmsg)) {
                    // Check if user was created despite error (duplicate username case)
                    $existing_user_id = get_user_id($name);
                    if ($existing_user_id) {
                        // User exists - use existing user
                        $xi_user_id = $existing_user_id;
                        $shared_account_was_created = false;
                        log_sso('process_shared_account_group: Shared account user "' . $name . '" already exists with username "' . $name . '", using existing user ID ' . $xi_user_id, 'info');
                    } else {
                        // User doesn't exist and creation failed
                        throw new Exception('Failed to create shared account user: ' . implode(', ', $errmsg));
                    }
                } else {
                    log_sso('process_shared_account_group: Created shared account user - Name: "' . $name . '", ID: ' . $xi_user_id, 'info');
                }
                
                // Set user meta and attributes only for newly created users
                if ($shared_account_was_created) {
                    $user_meta = array(
                        'name' => $name,
                        'mobile_number' => grab_array_var($form_data, 'phone'),
                        'enable_notifications' => normalize_boolean_flag(grab_array_var($form_data, 'enable_notifications', 0)),
                        'language' => grab_array_var($form_data, 'language'),
                        'date_format' => grab_array_var($form_data, 'date_format'),
                        'number_format' => grab_array_var($form_data, 'number_format'),
                        'week_format' => grab_array_var($form_data, 'week_format'),
                        'userlevel' => $level,
                        'authorized_for_all_objects' => normalize_boolean_flag(grab_array_var($form_data, 'see_hosts_services', 0)),
                        'authorized_for_all_object_commands' => normalize_boolean_flag(grab_array_var($form_data, 'control_hosts_services', 0)),
                        'authorized_to_configure_objects' => normalize_boolean_flag(grab_array_var($form_data, 'configure_hosts_services', 0)),
                        'advanced_user' => normalize_boolean_flag(grab_array_var($form_data, 'advanced_user', 0)),
                        'authorized_for_monitoring_system' => normalize_boolean_flag(grab_array_var($form_data, 'monitoring_engine', 0)),
                        'readonly_user' => normalize_boolean_flag(grab_array_var($form_data, 'read_only', 0)),
                        'api_enabled' => normalize_boolean_flag(grab_array_var($form_data, 'api_enabled', 0)),
                        'autodeploy_access' => normalize_boolean_flag(grab_array_var($form_data, 'autodeploy_access', 0)),
                        'nagios_mod_gearman_access' => normalize_boolean_flag(grab_array_var($form_data, 'nagios_mod_gearman_access', 0)),
                        'ccm_access' => grab_array_var($form_data, 'core_config_manager_access', 0),
                        'tours' => serialize(array('new_user' => 1)),
                    );

                    $user_attributes = array(
                        'user_id' => $xi_user_id,
                        'enabled' => normalize_boolean_flag(grab_array_var($form_data, 'enable_account', 1)),
                    );

                    set_new_user_meta_helper($user_meta, $user_attributes);
                }
            }
        } else {
            log_sso('process_shared_account_group: Using existing shared user ID: ' . $xi_user_id, 'info');
        }
    } else {
        log_sso('process_shared_account_group: Using pre-created shared user ID: ' . $xi_user_id, 'info');
    }

    // Link all users to the shared account
    $linked_count = 0;
    $skipped_count = 0;
    $users_linked_ids = array(); // Track user object_ids counted in users_linked
    
    foreach ($users as $user) {
        $object_id = grab_array_var($user, 'id', '');
        if (empty($object_id)) {
            $skipped_count++;
            $display_name = grab_array_var($user, 'displayName', 'Unknown User');
            $statistics['users_skipped_other'][] = array(
                'display_name' => $display_name,
                'object_id' => $object_id,
                'reason' => 'Missing object ID'
            );
            log_sso('process_shared_account_group: Skipped user with empty object_id - displayName: ' . $display_name, 'warning');
            continue;
        }

        try {
            // Check if user already has individual configuration
            $existing_mapping = get_sso_mapping($object_id, $client_id);
            if ($existing_mapping && $existing_mapping['priority'] === 'individual' && $existing_mapping['source'] === 'individual') {
                // Individual config exists - link group but keep individual priority and user_id
                // Don't mark as SSO-created since individual config already existed
                link_aad_user_to_xi_user($object_id, $client_id, $tenant_id, (int)$existing_mapping['user_id'], 'both', 'individual', $group_id, false);
            } else {
                // No individual config - use group mapping
                // Mark as SSO-created if shared account was newly created
                link_aad_user_to_xi_user($object_id, $client_id, $tenant_id, $xi_user_id, 'group', 'group', $group_id, $shared_account_was_created);
            }
            $linked_count++;
            $statistics['users_linked']++;
            $users_linked_ids[] = $object_id; // Track successfully linked user
        } catch (Exception $e) {
            $display_name = grab_array_var($user, 'displayName', 'Unknown');
            $statistics['users_skipped_other'][] = array(
                'display_name' => $display_name,
                'object_id' => $object_id,
                'reason' => 'Failed to link to shared account: ' . $e->getMessage()
            );
            $warnings[] = 'Warning: Failed to link AAD user "' . $display_name . '" (ID: ' . $object_id . ') to shared account: ' . $e->getMessage();
            log_sso('process_shared_account_group: Failed to link AAD user "' . $display_name . '" (ID: ' . $object_id . ') to shared account: ' . $e->getMessage(), 'error');
            $skipped_count++;
        }
    }
    
    // Log comprehensive statistics
    log_sso('process_shared_account_group: Summary - Linked: ' . $statistics['users_linked'] . 
                  ', Skipped: ' . count($statistics['users_skipped_other']) . 
                  ', Shared account created: ' . ($statistics['shared_account_created'] ? 'Yes' : 'No'), 'info');
    
    // Log each skipped user
    foreach ($statistics['users_skipped_other'] as $skipped_user) {
        log_sso('process_shared_account_group: Skipped user - Name: "' . $skipped_user['display_name'] . '", ID: ' . $skipped_user['object_id'] . ', Reason: ' . $skipped_user['reason'], 'warning');
    }
    
    if ($skipped_count > 0) {
        $warnings[] = 'Warning: Skipped linking ' . $skipped_count . ' user(s) to shared account. ' . $linked_count . ' user(s) linked successfully.';
    }

    // Return user IDs for deduplication by category
    $statistics['users_linked_ids'] = $users_linked_ids;

    return array('xi_user_id' => $xi_user_id, 'warnings' => $warnings, 'statistics' => $statistics);
}

function process_individual_account_group($group_ids, $client_id, $tenant_id, $config, $users)
{
    $warnings = array();
    $created_count = 0;
    $skipped_count = 0;
    
    // Extract form_data for user configuration fields
    $form_data = grab_array_var($config, 'form_data', array());
    
    // Structured statistics
    $statistics = array(
        'users_created' => 0,
        'users_skipped_no_email' => array(),
        'users_skipped_duplicate' => array(),
        'users_skipped_other' => array(),
        'users_linked_existing' => 0
    );
    
    // Check top-level namingTemplate first (for shared mode), then form_data
    $naming_template = grab_array_var($config, 'namingTemplate', '');
    if (empty($naming_template)) {
        $naming_template = grab_array_var($form_data, 'namingTemplate', '');
    }
    $tenant_name = grab_array_var($config, 'tenant_name', '');
    $group_name = grab_array_var($config, 'group_name', '');

    if (empty($tenant_name)) {
        try {
            $tenant_details = get_tenants_credentials_helper($client_id);
            $tenant_name = grab_array_var($tenant_details, 'name', 'SSO');
        } catch (Exception $e) {
            log_sso('Failed to get tenant name: ' . $e->getMessage(), 'warning');
            $tenant_name = 'SSO';
        }
    }

    // Get group name if not provided - check group_names map first, then fall back to generic
    if (empty($group_name) && !empty($group_ids)) {
        // Check if group_names map is provided and has an entry for the first group
        $group_names = grab_array_var($config, 'group_names', array());
        if (!empty($group_names) && is_array($group_names)) {
            $first_group_id = $group_ids[0];
            if (isset($group_names[$first_group_id])) {
                $group_name = $group_names[$first_group_id];
            }
        }
        
        // If still empty, fall back to generic name
        if (empty($group_name)) {
            $group_name = 'Group';
        }
    }

    $existing_users = get_users();
    $user_ids_xi = array_map('intval', array_column($existing_users, 'user_id'));
    $users_created_ids = array(); // Track user object_ids counted in users_created
    $users_linked_existing_ids = array(); // Track user object_ids counted in users_linked_existing

    foreach ($users as $user) {
        $object_id = grab_array_var($user, 'id', '');
        if (empty($object_id)) {
            $skipped_count++;
            $display_name = grab_array_var($user, 'displayName', 'Unknown User');
            $statistics['users_skipped_other'][] = array(
                'display_name' => $display_name,
                'object_id' => $object_id,
                'reason' => 'Missing object ID'
            );
            log_sso('Skipping user with empty object_id - displayName: ' . $display_name, 'warning');
            continue;
        }

        // Validate user has required email
        $email = grab_array_var($user, 'mail', grab_array_var($user, 'userPrincipalName', ''));
        if (empty($email)) {
            $display_name = grab_array_var($user, 'displayName', 'Unknown User');
            $statistics['users_skipped_no_email'][] = array(
                'display_name' => $display_name,
                'object_id' => $object_id,
                'reason' => 'Missing email address'
            );
            $warnings[] = 'Warning: Skipped AAD user "' . $display_name . '" (ID: ' . $object_id . ') - missing email address';
            log_sso('Skipped AAD user "' . $display_name . '" (ID: ' . $object_id . ') - missing email address', 'warning');
            $skipped_count++;
            continue;
        }

        // Check if user already has individual configuration
        $existing_mapping = get_sso_mapping($object_id, $client_id);
        if ($existing_mapping && $existing_mapping['priority'] === 'individual' && $existing_mapping['source'] === 'individual') {
            // User has individual config - link to group but keep individual priority
            // Don't mark as SSO-created since individual config already existed
            try {
                link_aad_user_to_xi_user($object_id, $client_id, $tenant_id, (int)$existing_mapping['user_id'], 'both', 'individual', $group_ids[0], false);
                $statistics['users_linked_existing']++;
                $users_linked_existing_ids[] = $object_id; // Track successfully linked existing user
            } catch (Exception $e) {
                $display_name = grab_array_var($user, 'displayName', 'Unknown');
                $statistics['users_skipped_other'][] = array(
                    'display_name' => $display_name,
                    'object_id' => $object_id,
                    'reason' => 'Failed to link existing user to group: ' . $e->getMessage()
                );
                $warnings[] = 'Warning: Failed to link existing user "' . $display_name . '" (ID: ' . $object_id . ') to group: ' . $e->getMessage();
                log_sso('Failed to link existing user "' . $display_name . '" (ID: ' . $object_id . ') to group: ' . $e->getMessage(), 'error');
            }
            continue;
        }

        // Process naming template
        $username = '';
        if (!empty($naming_template)) {
            // Prepare user data for template processing
            $user_data = array(
                'name' => grab_array_var($user, 'displayName', ''),
                'displayName' => grab_array_var($user, 'displayName', ''),
                'email' => $email,
                'givenName' => grab_array_var($user, 'givenName', ''),
                'surname' => grab_array_var($user, 'surname', ''),
            );
            
            // Extract first and last name from displayName if not provided
            if (empty($user_data['givenName']) && !empty($user_data['displayName'])) {
                $name_parts = explode(' ', $user_data['displayName'], 2);
                $user_data['givenName'] = $name_parts[0] ?? '';
                $user_data['surname'] = $name_parts[1] ?? '';
            }
            
            $username = process_naming_template($naming_template, $user_data, $tenant_name, $group_name);
        }

        if (empty($username)) {
            $display_name = grab_array_var($user, 'displayName', 'User');
            $username = $tenant_name . ' -- ' . $display_name;
        }
        $username = sanitize_username_for_nagios($username, $tenant_name . ' -- User');

        // Create XI user
        $xi_user_id = null;
        $password = bin2hex(random_bytes(16));
        $level = (strcasecmp(grab_array_var($form_data, 'auth_level', 'User'), 'Admin') === 0) ? 255 : 1;
        $add_contact = normalize_boolean_flag(grab_array_var($form_data, 'add_contact', 0));
        $api_enabled = normalize_boolean_flag(grab_array_var($form_data, 'api_enabled', 0));
        $errmsg = array();

        try {
            $xi_user_id = add_user_account($username, $password, $username, $email, $level, 0, $add_contact, $api_enabled, $errmsg);

            // Handle duplicate username
            if ($errmsg && !empty($errmsg)) {
                $is_duplicate = false;
                foreach ($errmsg as $error_msg) {
                    if (stripos($error_msg, 'already exists') !== false || stripos($error_msg, 'username already') !== false) {
                        $is_duplicate = true;
                        break;
                    }
                }

                if ($is_duplicate) {
                    // First check if user with exact username already exists
                    $existing_user_id = get_user_id($username);
                    if ($existing_user_id) {
                        // User already exists - link AAD user to existing user
                        $xi_user_id = $existing_user_id;
                        $display_name = grab_array_var($user, 'displayName', 'Unknown');
                        log_sso('process_individual_account_group: User "' . $display_name . '" (AAD ID: ' . $object_id . ') already exists with username "' . $username . '", linking to existing user ID ' . $xi_user_id, 'info');
                        
                        // Link user to existing XI account
                        // Don't mark as SSO-created since user already existed
                        link_aad_user_to_xi_user($object_id, $client_id, $tenant_id, $xi_user_id, 'group', 'group', $group_ids[0], false);
                        $statistics['users_linked_existing']++;
                        $users_linked_existing_ids[] = $object_id; // Track successfully linked existing user
                        continue; // Skip to next user
                    }
                    
                    // User doesn't exist with exact username, try variants
                    $base_username = sanitize_username_for_nagios($username, $tenant_name . ' -- User', false);
                    $suffix = 1;
                    
                    while ($suffix <= 100) {
                        $candidate = $base_username . '-' . $suffix;
                        $errmsg = array();
                        $test_id = add_user_account($candidate, $password, $candidate, $email, $level, 0, $add_contact, $api_enabled, $errmsg);
                        if ($test_id && empty($errmsg)) {
                            $username = $candidate;
                            $xi_user_id = $test_id;
                            break;
                        }
                        $suffix++;
                    }
                }

                if (!$xi_user_id) {
                    // Variant creation failed - check if original username exists before skipping
                    $existing_user_id = get_user_id($username);
                    if ($existing_user_id) {
                        // User already exists - link AAD user to existing user
                        $xi_user_id = $existing_user_id;
                        $display_name = grab_array_var($user, 'displayName', 'Unknown');
                        log_sso('process_individual_account_group: User "' . $display_name . '" (AAD ID: ' . $object_id . ') already exists with username "' . $username . '", linking to existing user ID ' . $xi_user_id, 'info');
                        
                        // Link user to existing XI account
                        // Don't mark as SSO-created since user already existed
                        link_aad_user_to_xi_user($object_id, $client_id, $tenant_id, $xi_user_id, 'group', 'group', $group_ids[0], false);
                        $statistics['users_linked_existing']++;
                        $users_linked_existing_ids[] = $object_id; // Track successfully linked existing user
                        continue; // Skip to next user
                    }
                    
                    // No existing user found - skip this user
                    $display_name = grab_array_var($user, 'displayName', 'Unknown');
                    $error_reason = implode(', ', $errmsg);
                    $statistics['users_skipped_other'][] = array(
                        'display_name' => $display_name,
                        'object_id' => $object_id,
                        'reason' => 'Failed to create XI user: ' . $error_reason
                    );
                    $warnings[] = 'Warning: Failed to create XI user for AAD user "' . $display_name . '" (ID: ' . $object_id . '): ' . $error_reason;
                    log_sso('Failed to create XI user for AAD user "' . $display_name . '" (ID: ' . $object_id . '): ' . $error_reason, 'error');
                    $skipped_count++;
                    continue;
                }
            }

            if (!$xi_user_id) {
                $display_name = grab_array_var($user, 'displayName', 'Unknown');
                $statistics['users_skipped_other'][] = array(
                    'display_name' => $display_name,
                    'object_id' => $object_id,
                    'reason' => 'Failed to create XI user (unknown error)'
                );
                $warnings[] = 'Warning: Failed to create XI user for AAD user "' . $display_name . '" (ID: ' . $object_id . ')';
                log_sso('Failed to create XI user for AAD user "' . $display_name . '" (ID: ' . $object_id . ')', 'error');
                $skipped_count++;
                continue;
            }

            // Set user meta and attributes
            $user_meta = array(
                'name' => $username,
                'mobile_number' => grab_array_var($form_data, 'phone'),
                'enable_notifications' => normalize_boolean_flag(grab_array_var($form_data, 'enable_notifications', 0)),
                'language' => grab_array_var($form_data, 'language'),
                'date_format' => grab_array_var($form_data, 'date_format'),
                'number_format' => grab_array_var($form_data, 'number_format'),
                'week_format' => grab_array_var($form_data, 'week_format'),
                'userlevel' => $level,
                'authorized_for_all_objects' => normalize_boolean_flag(grab_array_var($form_data, 'see_hosts_services', 0)),
                'authorized_for_all_object_commands' => normalize_boolean_flag(grab_array_var($form_data, 'control_hosts_services', 0)),
                'authorized_to_configure_objects' => normalize_boolean_flag(grab_array_var($form_data, 'configure_hosts_services', 0)),
                'advanced_user' => normalize_boolean_flag(grab_array_var($form_data, 'advanced_user', 0)),
                'authorized_for_monitoring_system' => normalize_boolean_flag(grab_array_var($form_data, 'monitoring_engine', 0)),
                'readonly_user' => normalize_boolean_flag(grab_array_var($form_data, 'read_only', 0)),
                'api_enabled' => normalize_boolean_flag(grab_array_var($form_data, 'api_enabled', 0)),
                'autodeploy_access' => normalize_boolean_flag(grab_array_var($form_data, 'autodeploy_access', 0)),
                'nagios_mod_gearman_access' => normalize_boolean_flag(grab_array_var($form_data, 'nagios_mod_gearman_access', 0)),
                'ccm_access' => grab_array_var($form_data, 'core_config_manager_access', 0),
                'tours' => serialize(array('new_user' => 1)),
            );

            $user_attributes = array(
                'user_id' => $xi_user_id,
                'enabled' => normalize_boolean_flag(grab_array_var($form_data, 'enable_account', 1)),
            );

            set_new_user_meta_helper($user_meta, $user_attributes);

            // Link user to XI account
            // Mark as SSO-created since this is a newly created user
            link_aad_user_to_xi_user($object_id, $client_id, $tenant_id, $xi_user_id, 'group', 'group', $group_ids[0], true);
            $created_count++;
            $statistics['users_created']++;
            $users_created_ids[] = $object_id; // Track successfully created user
        } catch (Exception $e) {
            $display_name = grab_array_var($user, 'displayName', 'Unknown');
            $statistics['users_skipped_other'][] = array(
                'display_name' => $display_name,
                'object_id' => $object_id,
                'reason' => 'Exception: ' . $e->getMessage()
            );
            $warnings[] = 'Warning: Failed to process AAD user "' . $display_name . '" (ID: ' . $object_id . '): ' . $e->getMessage();
            log_sso('Exception processing AAD user "' . $display_name . '" (ID: ' . $object_id . '): ' . $e->getMessage(), 'error');
            $skipped_count++;
        }
    }
    
    // Log comprehensive statistics
    log_sso('process_individual_account_group: Summary - Created: ' . $statistics['users_created'] . 
                  ', Linked existing: ' . $statistics['users_linked_existing'] . 
                  ', Skipped (no email): ' . count($statistics['users_skipped_no_email']) . 
                  ', Skipped (other): ' . count($statistics['users_skipped_other']), 'info');
    
    // Log each skipped user
    foreach ($statistics['users_skipped_no_email'] as $skipped_user) {
        log_sso('process_individual_account_group: Skipped user (no email) - Name: "' . $skipped_user['display_name'] . '", ID: ' . $skipped_user['object_id'], 'warning');
    }
    foreach ($statistics['users_skipped_other'] as $skipped_user) {
        log_sso('process_individual_account_group: Skipped user (other) - Name: "' . $skipped_user['display_name'] . '", ID: ' . $skipped_user['object_id'] . ', Reason: ' . $skipped_user['reason'], 'warning');
    }
    
    if ($skipped_count > 0) {
        $warnings[] = 'Warning: Skipped ' . $skipped_count . ' user(s) during individual account creation. ' . $created_count . ' user(s) created successfully.';
    }

    // Return user IDs for deduplication by category
    $statistics['users_created_ids'] = $users_created_ids;
    $statistics['users_linked_existing_ids'] = $users_linked_existing_ids;

    return array('warnings' => $warnings, 'statistics' => $statistics);
}

function save_group_config_metadata($group_id, $client_id, $account_mode, $xi_user_id, $config, $member_count = 0)
{
    global $db_tables;

    $table = $db_tables[DB_NAGIOSXI]['sso_group_config'];
    $group_id_sql = escape_sql_param($group_id, DB_NAGIOSXI);
    $client_id_sql = escape_sql_param($client_id, DB_NAGIOSXI);
    $account_mode_sql = escape_sql_param($account_mode, DB_NAGIOSXI);
    $config_json = escape_sql_param(json_encode($config), DB_NAGIOSXI);
    $member_count = (int)$member_count;
    $xi_user_id_sql = is_null($xi_user_id) ? 'NULL' : (int)$xi_user_id;
    $db_type = get_nagiosxi_db_type();
    $quote = ($db_type === 'pgsql') ? '' : '`';
    $timestamp_func = ($db_type === 'pgsql') ? 'CURRENT_TIMESTAMP' : 'NOW()';

    if ($db_type === 'pgsql') {
        // PostgreSQL uses ON CONFLICT instead of ON DUPLICATE KEY
        $sql = "INSERT INTO {$table} (group_id, client_id, account_mode, xi_user_id, config_json, member_count, last_sync_at, created_at, updated_at)\n"
             . "VALUES ('{$group_id_sql}', '{$client_id_sql}', '{$account_mode_sql}', {$xi_user_id_sql}, '{$config_json}', {$member_count}, {$timestamp_func}, {$timestamp_func}, {$timestamp_func})\n"
             . "ON CONFLICT (group_id, client_id) DO UPDATE SET\n"
             . "    account_mode = EXCLUDED.account_mode,\n"
             . "    xi_user_id = EXCLUDED.xi_user_id,\n"
             . "    config_json = EXCLUDED.config_json,\n"
             . "    member_count = EXCLUDED.member_count,\n"
             . "    last_sync_at = {$timestamp_func},\n"
             . "    updated_at = {$timestamp_func}";
    } else {
        $sql = "INSERT INTO `{$table}` (`group_id`, `client_id`, `account_mode`, `xi_user_id`, `config_json`, `member_count`, `last_sync_at`, `created_at`, `updated_at`)\n"
             . "VALUES ('{$group_id_sql}', '{$client_id_sql}', '{$account_mode_sql}', {$xi_user_id_sql}, '{$config_json}', {$member_count}, {$timestamp_func}, {$timestamp_func}, {$timestamp_func})\n"
             . "ON DUPLICATE KEY UPDATE\n"
             . "    `account_mode` = VALUES(`account_mode`),\n"
             . "    `xi_user_id` = VALUES(`xi_user_id`),\n"
             . "    `config_json` = VALUES(`config_json`),\n"
             . "    `member_count` = VALUES(`member_count`),\n"
             . "    `last_sync_at` = {$timestamp_func},\n"
             . "    `updated_at` = {$timestamp_func}";
    }

    exec_sql_query(DB_NAGIOSXI, $sql);
}

function get_configured_groups()
{
    check_nagios_session_protector();
    
    $client_id = grab_request_var('client_id', '');
    $nsp = grab_request_var('nsp', '');
    
    if (empty($client_id)) {
        echo json_encode(array('status' => 'error', 'message' => 'client_id is required'));
        exit();
    }
    
    global $db_tables;
    $table = $db_tables[DB_NAGIOSXI]['sso_group_config'];
    $client_id_sql = escape_sql_param($client_id, DB_NAGIOSXI);
    $db_type = get_nagiosxi_db_type();
    
    $sql = ($db_type === 'pgsql'
        ? "SELECT * FROM {$table} WHERE client_id = '{$client_id_sql}' ORDER BY created_at DESC"
        : "SELECT * FROM `{$table}` WHERE `client_id` = '{$client_id_sql}' ORDER BY `created_at` DESC");
    
    $rs = exec_sql_query(DB_NAGIOSXI, $sql, true, false);
    
    $configured_groups = array();
    $configured_group_ids = array();
    if ($rs) {
        while (!$rs->EOF) {
            $config_json = $rs->fields['config_json'];
            $config = json_decode($config_json, true);
            
            $configured_groups[] = array(
                'group_id' => $rs->fields['group_id'],
                'client_id' => $rs->fields['client_id'],
                'account_mode' => $rs->fields['account_mode'],
                'xi_user_id' => $rs->fields['xi_user_id'],
                'member_count' => $rs->fields['member_count'],
                'last_sync_at' => $rs->fields['last_sync_at'],
                'created_at' => $rs->fields['created_at'],
                'config' => $config,
                'configured_user_count' => 0
            );
            $configured_group_ids[] = $rs->fields['group_id'];
            $rs->MoveNext();
        }
    }

    if (!empty($configured_group_ids)) {
        $configured_counts = array_fill_keys($configured_group_ids, 0);
        $mappings_table = $db_tables[DB_NAGIOSXI]['sso_mappings'];
        $mappings_sql = ($db_type === 'pgsql'
            ? "SELECT source_group_id FROM {$mappings_table} WHERE client_id = '{$client_id_sql}' AND source_group_id IS NOT NULL"
            : "SELECT `source_group_id` FROM `{$mappings_table}` WHERE `client_id` = '{$client_id_sql}' AND `source_group_id` IS NOT NULL");

        $mappings_rs = exec_sql_query(DB_NAGIOSXI, $mappings_sql, true, false);

        if ($mappings_rs) {
            while (!$mappings_rs->EOF) {
                $source_group_id_raw = $mappings_rs->fields['source_group_id'];

                if ($source_group_id_raw !== null && $source_group_id_raw !== '') {
                    $decoded = json_decode($source_group_id_raw, true);
                    $json_error = json_last_error();
                    $group_ids = array();

                    if ($json_error === JSON_ERROR_NONE) {
                        if (is_array($decoded)) {
                            foreach ($decoded as $value) {
                                if (is_string($value) || is_numeric($value)) {
                                    $group_ids[] = (string) $value;
                                }
                            }
                        } elseif (is_string($decoded) || is_numeric($decoded)) {
                            $group_ids[] = (string) $decoded;
                        }
                    } else {
                        $group_ids[] = $source_group_id_raw;
                    }

                    foreach ($group_ids as $gid) {
                        if ($gid !== '' && isset($configured_counts[$gid])) {
                            $configured_counts[$gid]++;
                        }
                    }
                }

                $mappings_rs->MoveNext();
            }
        }

        foreach ($configured_groups as &$group) {
            $group_id = $group['group_id'];
            $group['configured_user_count'] = isset($configured_counts[$group_id]) ? $configured_counts[$group_id] : 0;
        }
        unset($group);
    }
    
    echo json_encode(array('status' => 'success', 'data' => $configured_groups));
    exit();
}

function delete_group_configuration()
{
    check_nagios_session_protector();
    
    $group_id = grab_request_var('group_id', '');
    $client_id = grab_request_var('client_id', '');
    
    if (empty($group_id) || empty($client_id)) {
        echo json_encode(array('status' => 'error', 'message' => 'group_id and client_id are required'));
        exit();
    }
    
    global $db_tables;
    
    // Delete group configuration metadata
    $table = $db_tables[DB_NAGIOSXI]['sso_group_config'];
    $group_id_sql = escape_sql_param($group_id, DB_NAGIOSXI);
    $client_id_sql = escape_sql_param($client_id, DB_NAGIOSXI);
    $db_type = get_nagiosxi_db_type();
    
    $sql = ($db_type === 'pgsql'
        ? "DELETE FROM {$table} WHERE group_id = '{$group_id_sql}' AND client_id = '{$client_id_sql}'"
        : "DELETE FROM `{$table}` WHERE `group_id` = '{$group_id_sql}' AND `client_id` = '{$client_id_sql}'");
    
    exec_sql_query(DB_NAGIOSXI, $sql);
    
    // Get all user mappings for this group
    // Need to find users where source_group_id contains this group_id (handles JSON arrays and legacy single values)
    $mappings_table = $db_tables[DB_NAGIOSXI]['sso_mappings'];
    
    if ($db_type === 'pgsql') {
        // PostgreSQL: Check if JSON array contains the group_id, or exact match for legacy single values
        // Use jsonb @> operator to check if array contains the value
        $group_id_json_escaped = escape_sql_param(json_encode(array($group_id_sql)), DB_NAGIOSXI);
        $sql = "SELECT object_id FROM {$mappings_table} WHERE client_id = '{$client_id_sql}' AND (\n"
             . "    (source_group_id::jsonb @> '{$group_id_json_escaped}'::jsonb)\n"
             . "    OR source_group_id = '{$group_id_sql}'\n"
             . ")";
    } else {
        // MySQL: Use JSON_CONTAINS for JSON arrays, or exact match for legacy single values
        // JSON_CONTAINS returns true if the first JSON document contains the second
        $sql = "SELECT `object_id` FROM `{$mappings_table}` WHERE `client_id` = '{$client_id_sql}' AND (\n"
             . "    (JSON_CONTAINS(`source_group_id`, JSON_QUOTE('{$group_id_sql}')) = 1)\n"
             . "    OR `source_group_id` = '{$group_id_sql}'\n"
             . ")";
    }
    
    $rs = exec_sql_query(DB_NAGIOSXI, $sql, true, false);
    
    $affected_users = 0;
    if ($rs) {
        while (!$rs->EOF) {
            $object_id = $rs->fields['object_id'];
            remove_group_sso_config($object_id, $client_id, $group_id);
            $affected_users++;
            $rs->MoveNext();
        }
    }
    
    echo json_encode(array(
        'status' => 'success', 
        'message' => 'Group configuration deleted successfully. ' . $affected_users . ' user mapping(s) updated.',
        'affected_users' => $affected_users
    ));
    exit();
}

