<?php

namespace GriffinForms\Admin\App\Html\Modals\Accounts;

use GriffinForms\Includes\Integrations\FormSettingsManager;
use GriffinForms\Admin\Choices\Form as FormChoices;
use GriffinForms\Admin\App\FormControls\Factory as ControlFactory;

trait Responses
{
    public function getAccountRegistrationConfig()
    {
        $this->response = array('success' => false);
        $this->checkRequest('get_account_registration_config');

        $form_id = isset($_POST['form_id']) ? absint(wp_unslash($_POST['form_id'])) : 0;
        if ($form_id <= 0) {
            wp_send_json_error(array(
                'msg' => __('Invalid form context supplied.', 'griffinforms-form-builder'),
            ));
        }

        $manager = FormSettingsManager::getInstance();
        $stored = $manager->getIntegrationSettings($form_id, 'wordpress_accounts');
        $payload = $this->normalizeStoredPayload($stored);

        wp_send_json_success(array(
            'msg' => __('User registration settings loaded.', 'griffinforms-form-builder'),
            'settings' => $payload,
        ));
    }

    public function getAccountRuleEditorData()
    {
        $this->response = array('success' => false);
        $this->checkRequest('get_account_rule_editor_data');

        $form_id = isset($_POST['form_id']) ? absint(wp_unslash($_POST['form_id'])) : 0;
        if ($form_id <= 0) {
            wp_send_json_error(array(
                'msg' => __('Invalid form context supplied.', 'griffinforms-form-builder'),
            ));
        }

        $editor_data = array(
            'fields' => $this->getFormFieldOptions($form_id),
            'messages' => $this->getMessageOptions(),
            'roles' => $this->getRoleOptions(),
            'creation_modes' => array(
                'immediate_create' => __('Create automatically', 'griffinforms-form-builder'),
                'pending_admin_activation' => __('Approve before creating the user', 'griffinforms-form-builder'),
                'manual_review_only' => __('Review first, then decide', 'griffinforms-form-builder'),
            ),
            'existing_account_behaviors' => array(
                'error' => __('Block this sign-up', 'griffinforms-form-builder'),
                'update_user' => __('Update the existing user', 'griffinforms-form-builder'),
                'accept_noop' => __('Keep the submission only', 'griffinforms-form-builder'),
            ),
            'password_modes' => array(
                'mapped_password' => __('Use a password field', 'griffinforms-form-builder'),
                'generate_password' => __('Send password setup email', 'griffinforms-form-builder'),
            ),
            'username_modes' => array(
                'mapped_or_generate' => __('Use a username field', 'griffinforms-form-builder'),
                'generate_only' => __('Generate automatically', 'griffinforms-form-builder'),
            ),
            'mapping_modes' => array(
                'none' => __('Do not save it', 'griffinforms-form-builder'),
                'map_field' => __('Save it from a field', 'griffinforms-form-builder'),
            ),
        );

        wp_send_json_success(array(
            'msg' => __('User registration data loaded.', 'griffinforms-form-builder'),
            'editor_data' => $editor_data,
        ));
    }

    public function getAccountRulesListHtml()
    {
        $this->response = array('success' => false);
        $this->checkRequest('get_account_rules_list_html');

        $form_id = isset($_POST['form_id']) ? absint(wp_unslash($_POST['form_id'])) : 0;
        $raw_settings = isset($_POST['settings']) ? wp_unslash($_POST['settings']) : '';
        $is_persisted_rule = isset($_POST['is_persisted_rule']) && wp_unslash($_POST['is_persisted_rule']) === '1';

        if ($form_id <= 0) {
            wp_send_json_error(array(
                'msg' => __('Invalid form context supplied.', 'griffinforms-form-builder'),
            ));
        }

        $settings = is_string($raw_settings) ? json_decode($raw_settings, true) : array();
        if (!is_array($settings)) {
            $settings = array();
        }

        $settings = $this->normalizeIncomingSettings($settings);
        $html = $this->renderAccountsRulesListHtml($form_id, $settings['rules'] ?? array());

        wp_send_json_success(array(
            'msg' => __('User registration list loaded.', 'griffinforms-form-builder'),
            'html' => $html,
        ));
    }

    public function getAccountRuleEditorHtml()
    {
        $this->response = array('success' => false);
        $this->checkRequest('get_account_rule_editor_html');

        $form_id = isset($_POST['form_id']) ? absint(wp_unslash($_POST['form_id'])) : 0;
        $raw_rule = isset($_POST['rule']) ? wp_unslash($_POST['rule']) : '';
        $raw_settings = isset($_POST['settings']) ? wp_unslash($_POST['settings']) : '';
        $is_persisted_rule = isset($_POST['is_persisted_rule']) && wp_unslash($_POST['is_persisted_rule']) === '1';

        if ($form_id <= 0) {
            wp_send_json_error(array(
                'msg' => __('Invalid form context supplied.', 'griffinforms-form-builder'),
            ));
        }

        $rule = is_string($raw_rule) ? json_decode($raw_rule, true) : array();
        if (!is_array($rule)) {
            $rule = array();
        }

        $settings = is_string($raw_settings) ? json_decode($raw_settings, true) : array();
        if (!is_array($settings)) {
            $settings = array();
        }

        $rule = $this->normalizeIncomingSettings(array('rules' => array($rule)));
        $rule = $rule['rules'][0] ?? $this->getDefaultRule();
        $settings = $this->normalizeIncomingSettings($settings);

        $html = $this->renderAccountRuleEditorHtml($form_id, $rule, $settings['rules'] ?? array(), $is_persisted_rule);

        wp_send_json_success(array(
            'msg' => __('User registration editor loaded.', 'griffinforms-form-builder'),
            'html' => $html,
            'rule' => $rule,
        ));
    }

    public function saveAccountRegistrationConfig()
    {
        $this->response = array('success' => false);
        $this->checkRequest('save_account_registration_config');

        $form_id = isset($_POST['form_id']) ? absint(wp_unslash($_POST['form_id'])) : 0;
        $raw_payload = isset($_POST['payload']) ? wp_unslash($_POST['payload']) : '';

        if ($form_id <= 0) {
            wp_send_json_error(array(
                'msg' => __('Invalid form context supplied.', 'griffinforms-form-builder'),
            ));
        }

        $decoded = is_string($raw_payload) ? json_decode($raw_payload, true) : array();
        if (!is_array($decoded)) {
            $decoded = array();
        }

        $normalized_settings = $this->normalizeIncomingSettings($decoded);
        $validation = $this->validateAccountSettings($form_id, $normalized_settings);
        if (is_wp_error($validation)) {
            wp_send_json_error(array(
                'msg' => $validation->get_error_message(),
            ));
        }

        $payload = array(
            'mode' => 'custom',
            'settings' => $validation,
            'updated_at' => gmdate('c'),
            'updated_by' => get_current_user_id(),
        );

        $manager = FormSettingsManager::getInstance();
        $save_result = $manager->saveIntegrationSettings($form_id, 'wordpress_accounts', $payload);

        if (is_wp_error($save_result)) {
            wp_send_json_error(array(
                'msg' => $save_result->get_error_message(),
            ));
        }

        if (is_object($this->log)) {
            $this->log->add(
                'info',
                'form',
                $form_id,
                (int) get_current_user_id(),
                __('User registration settings saved.', 'griffinforms-form-builder'),
                'audit'
            );
        }

        $this->fireAccountsModifiedHook($form_id, 'save_config');

        wp_send_json_success(array(
            'msg' => __('User registration settings saved.', 'griffinforms-form-builder'),
            'form_id' => $form_id,
            'settings' => $payload,
        ));
    }

    public function deleteAccountRegistrationRule()
    {
        $this->response = array('success' => false);
        $this->checkRequest('delete_account_registration_rule');

        $form_id = isset($_POST['form_id']) ? absint(wp_unslash($_POST['form_id'])) : 0;
        $rule_id = isset($_POST['rule_id']) ? sanitize_text_field(wp_unslash($_POST['rule_id'])) : '';

        if ($form_id <= 0 || $rule_id === '') {
            wp_send_json_error(array(
                'msg' => __('Invalid user registration reference supplied.', 'griffinforms-form-builder'),
            ));
        }

        $manager = FormSettingsManager::getInstance();
        $stored = $manager->getIntegrationSettings($form_id, 'wordpress_accounts');
        $payload = $this->normalizeStoredPayload($stored);
        $rules = isset($payload['settings']['rules']) && is_array($payload['settings']['rules']) ? $payload['settings']['rules'] : array();

        $updated_rules = array_values(array_filter($rules, static function ($rule) use ($rule_id) {
            return !is_array($rule) || (($rule['id'] ?? '') !== $rule_id);
        }));

        if (count($updated_rules) === count($rules)) {
            wp_send_json_error(array(
                'msg' => __('The requested user registration could not be found.', 'griffinforms-form-builder'),
            ));
        }

        $payload['settings']['rules'] = $updated_rules;
        $payload['updated_at'] = gmdate('c');
        $payload['updated_by'] = get_current_user_id();

        $save_result = $manager->saveIntegrationSettings($form_id, 'wordpress_accounts', $payload);
        if (is_wp_error($save_result)) {
            wp_send_json_error(array(
                'msg' => $save_result->get_error_message(),
            ));
        }

        if (is_object($this->log)) {
            $this->log->add(
                'info',
                'form',
                $form_id,
                (int) get_current_user_id(),
                __('User registration deleted.', 'griffinforms-form-builder'),
                'audit'
            );
        }

        $this->fireAccountsModifiedHook($form_id, 'delete_rule');

        wp_send_json_success(array(
            'msg' => __('User registration deleted.', 'griffinforms-form-builder'),
            'form_id' => $form_id,
            'settings' => $payload,
        ));
    }

    protected function normalizeStoredPayload($stored)
    {
        if (!is_array($stored)) {
            $stored = array();
        }

        $mode = isset($stored['mode']) ? sanitize_text_field((string) $stored['mode']) : 'custom';
        $settings = isset($stored['settings']) && is_array($stored['settings']) ? $stored['settings'] : array();

        return array(
            'mode' => $mode,
            'settings' => $this->normalizeIncomingSettings($settings),
            'updated_at' => isset($stored['updated_at']) ? sanitize_text_field((string) $stored['updated_at']) : null,
            'updated_by' => isset($stored['updated_by']) ? absint($stored['updated_by']) : null,
        );
    }

    protected function normalizeIncomingSettings(array $settings): array
    {
        $rules = isset($settings['rules']) && is_array($settings['rules']) ? $settings['rules'] : array();
        $normalized_rules = array();
        $mapping_specs = $this->getAccountMappingSpecs();

        foreach ($rules as $index => $rule) {
            if (!is_array($rule)) {
                continue;
            }

            $rule_id = isset($rule['id']) ? sanitize_key((string) $rule['id']) : '';
            if ($rule_id === '') {
                $rule_id = 'wa_rule_' . ($index + 1);
            }

            $normalized_rule = array(
                'id' => $rule_id,
                'enabled' => !empty($rule['enabled']),
                'email_field_id' => absint($rule['email_field_id'] ?? 0),
                'password_mode' => sanitize_text_field((string) ($rule['password_mode'] ?? 'generate_password')),
                'password_field_id' => absint($rule['password_field_id'] ?? 0),
                'username_mode' => sanitize_text_field((string) ($rule['username_mode'] ?? ((absint($rule['username_field_id'] ?? 0) > 0) ? 'mapped_or_generate' : 'generate_only'))),
                'username_field_id' => absint($rule['username_field_id'] ?? 0),
                'send_confirmation_email' => !empty($rule['send_confirmation_email']),
                'confirmation_message_id' => absint($rule['confirmation_message_id'] ?? 0),
                'role' => sanitize_key((string) ($rule['role'] ?? 'subscriber')),
                'creation_mode' => sanitize_text_field((string) ($rule['creation_mode'] ?? 'immediate_create')),
                'existing_account_behavior' => sanitize_text_field((string) ($rule['existing_account_behavior'] ?? 'error')),
            );

            foreach ($mapping_specs as $mapping_spec) {
                $field_key = $mapping_spec['field_key'];
                $mode_key = $mapping_spec['mode_key'];
                $default_mode = absint($rule[$field_key] ?? 0) > 0 ? 'map_field' : 'none';
                $normalized_rule[$mode_key] = sanitize_text_field((string) ($rule[$mode_key] ?? $default_mode));
                $normalized_rule[$field_key] = absint($rule[$field_key] ?? 0);
            }

            $normalized_rules[] = $normalized_rule;
        }

        return array(
            'rules' => array_values($normalized_rules),
        );
    }

    protected function validateAccountSettings(int $form_id, array $settings)
    {
        $rules = isset($settings['rules']) && is_array($settings['rules']) ? $settings['rules'] : array();
        $email_field_ids = array();
        $field_options = $this->getFormFieldOptions($form_id);
        $email_field_map = $this->getOptionIdMap($field_options['email_fields'] ?? array());
        $password_field_map = $this->getOptionIdMap($field_options['password_fields'] ?? array());
        $mapping_specs = $this->getAccountMappingSpecs();
        $field_maps = $this->getAccountMappingFieldMaps($field_options);

        foreach ($rules as $rule) {
            if (!is_array($rule)) {
                continue;
            }

            $email_field_id = absint($rule['email_field_id'] ?? 0);
            if ($email_field_id <= 0) {
                return new \WP_Error('missing_email_field', __('Each user registration must map to an email field.', 'griffinforms-form-builder'));
            }

            if (!isset($email_field_map[$email_field_id])) {
                return new \WP_Error('invalid_email_field', __('The selected email field is no longer available in this form. Choose another email field before saving.', 'griffinforms-form-builder'));
            }

            if (in_array($email_field_id, $email_field_ids, true)) {
                return new \WP_Error('duplicate_email_field', __('Only one user registration may target a given email field.', 'griffinforms-form-builder'));
            }

            $email_field_ids[] = $email_field_id;
            $is_iterable_email_field = !empty($email_field_map[$email_field_id]['is_iterable']);

            $password_mode = (string) ($rule['password_mode'] ?? '');
            if ($is_iterable_email_field && $password_mode === 'mapped_password') {
                return new \WP_Error('iterable_email_requires_generated_password', __('Iterable email fields can only use Email Password Setup Link. Change the password strategy before saving.', 'griffinforms-form-builder'));
            }

            if ($password_mode === 'mapped_password' && absint($rule['password_field_id'] ?? 0) <= 0) {
                return new \WP_Error('missing_password_field', __('Mapped password mode requires a password field.', 'griffinforms-form-builder'));
            }

            if ($password_mode === 'mapped_password' && !isset($password_field_map[absint($rule['password_field_id'] ?? 0)])) {
                return new \WP_Error('invalid_password_field', __('The selected password field is no longer available in this form. Review the password strategy before saving.', 'griffinforms-form-builder'));
            }

            $username_mode = (string) ($rule['username_mode'] ?? '');
            if ($username_mode === 'mapped_or_generate' && absint($rule['username_field_id'] ?? 0) <= 0) {
                return new \WP_Error('missing_username_field', __('Map Username Field requires a username field selection.', 'griffinforms-form-builder'));
            }

            if ($username_mode === 'mapped_or_generate' && !isset($field_maps['text_fields'][absint($rule['username_field_id'] ?? 0)])) {
                return new \WP_Error('invalid_username_field', __('The selected username field is no longer available in this form. Choose another field or generate the username automatically.', 'griffinforms-form-builder'));
            }

            if ($is_iterable_email_field && $username_mode === 'mapped_or_generate' && empty($field_maps['text_fields'][absint($rule['username_field_id'] ?? 0)]['is_iterable'])) {
                return new \WP_Error('iterable_email_requires_iterable_username_field', __('Iterable email fields can only map username from another iterable field. Choose an iterable username field or generate the username automatically before saving.', 'griffinforms-form-builder'));
            }

            $mapping_validation_error = $this->validateAccountMappingSelections(
                $rule,
                $mapping_specs,
                $field_maps,
                $is_iterable_email_field
            );
            if (is_wp_error($mapping_validation_error)) {
                return $mapping_validation_error;
            }

            if (!empty($rule['send_confirmation_email']) && absint($rule['confirmation_message_id'] ?? 0) <= 0) {
                return new \WP_Error('missing_confirmation_message', __('Send confirmation email requires a confirmation message selection.', 'griffinforms-form-builder'));
            }

            if (sanitize_key((string) ($rule['role'] ?? '')) === 'administrator') {
                return new \WP_Error('administrator_role_not_allowed', __('Administrator role is not available for account registration setups in V1. Choose another role before saving.', 'griffinforms-form-builder'));
            }
        }

        return array('rules' => $rules);
    }

    protected function getOptionIdMap(array $options): array
    {
        $id_map = array();

        foreach ($options as $option) {
            if (!is_array($option)) {
                continue;
            }

            $option_id = absint($option['id'] ?? 0);
            if ($option_id > 0) {
                $id_map[$option_id] = $option;
            }
        }

        return $id_map;
    }

    protected function getAccountMappingSpecs(): array
    {
        return array(
            array(
                'label' => __('First Name', 'griffinforms-form-builder'),
                'mode_key' => 'first_name_mode',
                'field_key' => 'first_name_field_id',
                'source_group' => 'text_fields',
                'mode_options' => 'mapping_mode_options',
                'placeholder' => __('Select a field', 'griffinforms-form-builder'),
                'missing_error_code' => 'missing_first_name_field',
                'missing_error_message' => __('First Name mapping requires a field selection.', 'griffinforms-form-builder'),
                'invalid_error_code' => 'invalid_first_name_field',
                'invalid_error_message' => __('The selected first name field is no longer available in this form. Choose another field before saving.', 'griffinforms-form-builder'),
                'iterable_error_code' => 'iterable_email_requires_iterable_first_name_field',
                'iterable_error_message' => __('Iterable email fields can only map first name from another iterable field. Choose an iterable first name field or stop mapping first name before saving.', 'griffinforms-form-builder'),
            ),
            array(
                'label' => __('Last Name', 'griffinforms-form-builder'),
                'mode_key' => 'last_name_mode',
                'field_key' => 'last_name_field_id',
                'source_group' => 'text_fields',
                'mode_options' => 'mapping_mode_options',
                'placeholder' => __('Select a field', 'griffinforms-form-builder'),
                'missing_error_code' => 'missing_last_name_field',
                'missing_error_message' => __('Last Name mapping requires a field selection.', 'griffinforms-form-builder'),
                'invalid_error_code' => 'invalid_last_name_field',
                'invalid_error_message' => __('The selected last name field is no longer available in this form. Choose another field before saving.', 'griffinforms-form-builder'),
                'iterable_error_code' => 'iterable_email_requires_iterable_last_name_field',
                'iterable_error_message' => __('Iterable email fields can only map last name from another iterable field. Choose an iterable last name field or stop mapping last name before saving.', 'griffinforms-form-builder'),
            ),
            array(
                'label' => __('Profile Image', 'griffinforms-form-builder'),
                'mode_key' => 'profile_image_mode',
                'field_key' => 'profile_image_field_id',
                'source_group' => 'file_fields',
                'mode_options' => 'profile_image_mode_options',
                'placeholder' => __('Select a file upload field', 'griffinforms-form-builder'),
                'missing_error_code' => 'missing_profile_image_field',
                'missing_error_message' => __('Profile Image mapping requires a field selection.', 'griffinforms-form-builder'),
                'invalid_error_code' => 'invalid_profile_image_field',
                'invalid_error_message' => __('The selected profile image field is no longer available in this form. Choose another file upload field or stop mapping profile image before saving.', 'griffinforms-form-builder'),
                'iterable_error_code' => 'iterable_email_disables_profile_image_mapping',
                'iterable_error_message' => __('Profile image mapping is not available when the selected email field is iterable. Stop mapping profile image before saving.', 'griffinforms-form-builder'),
                'disallow_when_email_iterable' => true,
            ),
        );
    }

    protected function getAccountMappingFieldMaps(array $field_options): array
    {
        $field_maps = array();
        foreach ($this->getAccountMappingSpecs() as $mapping_spec) {
            $source_group = $mapping_spec['source_group'];
            if (isset($field_maps[$source_group])) {
                continue;
            }

            $field_maps[$source_group] = $this->getOptionIdMap($field_options[$source_group] ?? array());
        }

        return $field_maps;
    }

    protected function validateAccountMappingSelections(array $rule, array $mapping_specs, array $field_maps, bool $is_iterable_email_field)
    {
        foreach ($mapping_specs as $mapping_spec) {
            $mode = (string) ($rule[$mapping_spec['mode_key']] ?? '');
            if ($mode !== 'map_field') {
                continue;
            }

            $field_id = absint($rule[$mapping_spec['field_key']] ?? 0);
            $source_group = $mapping_spec['source_group'];
            $field_map = $field_maps[$source_group] ?? array();

            if ($field_id <= 0) {
                return new \WP_Error($mapping_spec['missing_error_code'], $mapping_spec['missing_error_message']);
            }

            if (!isset($field_map[$field_id])) {
                return new \WP_Error($mapping_spec['invalid_error_code'], $mapping_spec['invalid_error_message']);
            }

            if (!$is_iterable_email_field) {
                continue;
            }

            if (!empty($mapping_spec['disallow_when_email_iterable'])) {
                return new \WP_Error($mapping_spec['iterable_error_code'], $mapping_spec['iterable_error_message']);
            }

            if (empty($field_map[$field_id]['is_iterable'])) {
                return new \WP_Error($mapping_spec['iterable_error_code'], $mapping_spec['iterable_error_message']);
            }
        }

        return true;
    }

    protected function isSelectedFieldIterable(array $fields, int $field_id): bool
    {
        if ($field_id <= 0) {
            return false;
        }

        foreach ($fields as $field) {
            if (!is_array($field)) {
                continue;
            }

            if (absint($field['id'] ?? 0) !== $field_id) {
                continue;
            }

            return !empty($field['is_iterable']);
        }

        return false;
    }

    protected function getFormFieldOptions(int $form_id): array
    {
        $form_sql = \GriffinForms\Admin\Sql\Form::getInstance();
        $rows = $form_sql->fieldsInForm($form_id);
        $options = array(
            'email_fields' => array(),
            'password_fields' => array(),
            'text_fields' => array(),
            'file_fields' => array(),
        );

        if (!is_array($rows)) {
            return $options;
        }

        foreach ($rows as $row) {
            if (!is_object($row) || empty($row->id)) {
                continue;
            }

            $iterations = isset($row->iterations) ? maybe_unserialize($row->iterations) : array();
            $is_iterable = is_array($iterations) && !empty($iterations['allow']);

            $field = array(
                'id' => (int) $row->id,
                'label' => $this->formatFieldLabel($row),
                'field_type' => isset($row->field_type) ? (string) $row->field_type : '',
                'is_iterable' => $is_iterable,
            );

            if ($field['field_type'] === 'inputemail') {
                $options['email_fields'][] = $field;
            }

            if ($field['field_type'] === 'inputpassword') {
                $options['password_fields'][] = $field;
            }

            if (in_array($field['field_type'], array('inputtext', 'inputemail', 'inputurl', 'textarea'), true)) {
                $options['text_fields'][] = $field;
            }

            if ($field['field_type'] === 'fileupload') {
                $options['file_fields'][] = $field;
            }
        }

        return $options;
    }

    protected function getMessageOptions(): array
    {
        global $wpdb;

        $table = $this->config->getTable('message');
        $rows = $wpdb->get_results(
            $wpdb->prepare(
                'SELECT id, name FROM %i ORDER BY id DESC',
                $table
            )
        );

        $options = array();
        if (!is_array($rows)) {
            return $options;
        }

        foreach ($rows as $row) {
            if (!is_object($row) || empty($row->id)) {
                continue;
            }

            $options[] = array(
                'id' => (int) $row->id,
                'label' => (string) ($row->name ?? ('#' . (int) $row->id)),
            );
        }

        return $options;
    }

    protected function getRoleOptions(): array
    {
        $choices = FormChoices::getInstance();
        $roles = method_exists($choices, 'getUserRoles') ? $choices->getUserRoles() : array();
        $options = array();

        foreach ((array) $roles as $role_key => $role_label) {
            $role_key = sanitize_key((string) $role_key);
            if ($role_key === 'administrator') {
                continue;
            }

            $options[] = array(
                'value' => $role_key,
                'label' => (string) $role_label,
            );
        }

        return $options;
    }

    protected function formatFieldLabel($row): string
    {
        $name = isset($row->name) ? (string) $row->name : '';
        $heading = isset($row->heading) ? (string) $row->heading : '';
        $type = isset($row->field_type) ? (string) $row->field_type : '';
        $primary = $heading !== '' ? $heading : $name;

        if ($primary === '') {
            $primary = sprintf(__('Field #%d', 'griffinforms-form-builder'), (int) $row->id);
        }

        return sprintf('%s (%s)', $primary, $type);
    }

    protected function fireAccountsModifiedHook(int $form_id, string $action): void
    {
        if ($form_id <= 0) {
            return;
        }

        do_action(
            'griffinforms_form_modified',
            $form_id,
            'accounts',
            array(
                'action' => $action,
                'integration_id' => 'wordpress_accounts',
            )
        );
    }

    protected function getDefaultRule(): array
    {
        return array(
            'id' => 'wa_rule_' . time(),
            'enabled' => true,
            'email_field_id' => 0,
            'password_mode' => 'generate_password',
            'password_field_id' => 0,
            'first_name_mode' => 'none',
            'first_name_field_id' => 0,
            'last_name_mode' => 'none',
            'last_name_field_id' => 0,
            'username_mode' => 'generate_only',
            'username_field_id' => 0,
            'profile_image_mode' => 'none',
            'profile_image_field_id' => 0,
            'send_confirmation_email' => true,
            'confirmation_message_id' => 0,
            'role' => 'subscriber',
            'creation_mode' => 'immediate_create',
            'existing_account_behavior' => 'error',
        );
    }

    protected function renderAccountRuleEditorHtml(int $form_id, array $rule, array $all_rules = array(), bool $is_persisted_rule = false): string
    {
        $field_options = $this->getFormFieldOptions($form_id);
        $field_options['email_fields'] = $this->filterAvailableEmailFields(
            $field_options['email_fields'] ?? array(),
            $all_rules,
            (string) ($rule['id'] ?? '')
        );
        $messages = $this->getMessageOptions();
        $roles = $this->getRoleOptions();
        $has_password_fields = !empty($field_options['password_fields']);
        $is_iterable_email_field = $this->isSelectedFieldIterable(
            $field_options['email_fields'] ?? array(),
            absint($rule['email_field_id'] ?? 0)
        );

        $message_map = array();
        foreach ($messages as $message) {
            $message_map[(string) ($message['id'] ?? '')] = (string) ($message['label'] ?? '');
        }

        $role_map = array();
        foreach ($roles as $role) {
            $role_map[(string) ($role['value'] ?? '')] = (string) ($role['label'] ?? '');
        }

        $password_mode_options = array(
            'generate_password' => array(
                'label' => __('Send password setup email', 'griffinforms-form-builder'),
            ),
            'mapped_password' => array(
                'label' => __('Use a password field', 'griffinforms-form-builder'),
                'disabled' => !$has_password_fields || $is_iterable_email_field,
            ),
        );

        $html  = '<div class="gf-accounts-enabled-toggle d-flex align-items-center justify-content-end mb-3">';
        $html .= $this->renderFactoryControl('checkbox', 'enabled', !empty($rule['enabled']), 'accounts', array(
            'checkbox_label' => __('Enable user registration', 'griffinforms-form-builder'),
            'container_classes' => 'mb-0',
            'attributes' => array('data-accounts-field' => 'enabled'),
        ));
        $html .= '</div>';

        $html .= $this->renderStageCard(
            'step_1',
            __('Who is signing up?', 'griffinforms-form-builder'),
            __('Choose the email field used for this user registration.', 'griffinforms-form-builder'),
            $this->renderFactoryControl('select', 'email_field_id', (string) ($rule['email_field_id'] ?? ''), 'accounts', array(
                'label' => __('Email Field', 'griffinforms-form-builder'),
                'options' => $this->mapOptionsForSelect($field_options['email_fields'] ?? array()),
                'placeholder' => __('Select an email field', 'griffinforms-form-builder'),
                'input_classes' => 'form-select form-select-sm',
                'attributes' => array('data-accounts-field' => 'email_field_id'),
            )),
            true
        );

        $html .= $this->renderStageCard(
            'step_2',
            __('When should the account be created?', 'griffinforms-form-builder'),
            __('Choose whether this user registration should create the user right away or wait for review.', 'griffinforms-form-builder'),
            '<div class="row g-3">' .
            $this->renderControlColumn(
                $this->renderFactoryControl('select', 'creation_mode', (string) ($rule['creation_mode'] ?? 'immediate_create'), 'accounts', array(
                    'label' => __('Registration Workflow', 'griffinforms-form-builder'),
                    'options' => array(
                        'immediate_create' => __('Create automatically', 'griffinforms-form-builder'),
                        'pending_admin_activation' => __('Approve before creating the user', 'griffinforms-form-builder'),
                        'manual_review_only' => __('Review first, then decide', 'griffinforms-form-builder'),
                    ),
                    'input_classes' => 'form-select form-select-sm',
                    'attributes' => array('data-accounts-field' => 'creation_mode'),
                )),
                'col-md-6'
            ) .
            $this->renderControlColumn(
                $this->renderFactoryControl('select', 'role', (string) ($rule['role'] ?? 'subscriber'), 'accounts', array(
                    'label' => __('Role', 'griffinforms-form-builder'),
                    'options' => $role_map,
                    'input_classes' => 'form-select form-select-sm',
                    'attributes' => array('data-accounts-field' => 'role'),
                )),
                'col-md-6'
            ) .
            '</div>',
            false
        );

        $html .= $this->renderStageCard(
            'step_3',
            __('How should access be set up?', 'griffinforms-form-builder'),
            __('Choose whether the person signing up enters a password or receives a password setup email.', 'griffinforms-form-builder'),
            '<div class="row g-3">' .
            $this->renderControlColumn(
                $this->renderFactoryControl('select', 'password_mode', (string) ($rule['password_mode'] ?? 'generate_password'), 'accounts', array(
                    'label' => __('Access Setup', 'griffinforms-form-builder'),
                    'options' => $password_mode_options,
                    'input_classes' => 'form-select form-select-sm',
                    'attributes' => array('data-accounts-field' => 'password_mode'),
                )),
                'col-md-6'
            ) .
            $this->renderControlColumn(
                $this->renderFactoryControl('select', 'password_field_id', (string) ($rule['password_field_id'] ?? ''), 'accounts', array(
                    'label' => __('Password Field', 'griffinforms-form-builder'),
                    'options' => $this->mapOptionsForSelect($field_options['password_fields'] ?? array()),
                    'placeholder' => __('Select a password field', 'griffinforms-form-builder'),
                    'input_classes' => 'form-select form-select-sm',
                    'container_classes' => 'gf-accounts-password-field-control',
                    'attributes' => array(
                        'data-accounts-field' => 'password_field_id',
                        'data-accounts-visible-when' => 'password_mode:mapped_password',
                    ),
                )),
                'col-md-6'
            ) .
            '</div>' .
            (!$has_password_fields ? '<div class="form-text mt-2 gf-accounts-password-field-note">' . esc_html__('This form does not currently have a password field, so the password setup email is the only available option.', 'griffinforms-form-builder') . '</div>' : '') .
            '<div class="form-text mt-2 gf-accounts-iterable-email-password-note' . (!$is_iterable_email_field ? ' d-none' : '') . '">' . esc_html__('This email field can create more than one user from a single submission, so the password setup email is required here.', 'griffinforms-form-builder') . '</div>',
            false
        );

        $mapping_mode_options = array(
            'none' => __('Do Not Map', 'griffinforms-form-builder'),
            'map_field' => __('Map Field', 'griffinforms-form-builder'),
        );
        $profile_image_mode_options = array(
            'none' => array(
                'label' => __('Do Not Map', 'griffinforms-form-builder'),
            ),
            'map_field' => array(
                'label' => __('Map Field', 'griffinforms-form-builder'),
                'disabled' => empty($field_options['file_fields']) || $is_iterable_email_field,
            ),
        );
        $mapping_specs = $this->getAccountMappingSpecs();
        $text_field_select_options = $this->mapOptionsForSelect($field_options['text_fields'] ?? array());

        $profile_mapping_rows  = '<div class="gf-accounts-optional-section-title fw-semibold mb-2">' . esc_html__('Profile details', 'griffinforms-form-builder') . '</div>';
        $profile_mapping_rows .= '<div class="text-muted small mb-3">' . esc_html__('Add these only if your form collects them. They are optional for user registration.', 'griffinforms-form-builder') . '</div>';
        $profile_mapping_rows .= '<div class="table-responsive">';
        $profile_mapping_rows .= '<table class="table table-sm align-middle mb-0 gf-accounts-mappings-table">';
        $profile_mapping_rows .= '<colgroup>';
        $profile_mapping_rows .= '<col style="width: 22%;">';
        $profile_mapping_rows .= '<col style="width: 32%;">';
        $profile_mapping_rows .= '<col style="width: 46%;">';
        $profile_mapping_rows .= '</colgroup>';
        $profile_mapping_rows .= '<thead><tr>';
        $profile_mapping_rows .= '<th>' . esc_html__('Field', 'griffinforms-form-builder') . '</th>';
        $profile_mapping_rows .= '<th>' . esc_html__('Strategy', 'griffinforms-form-builder') . '</th>';
        $profile_mapping_rows .= '<th>' . esc_html__('Source Field', 'griffinforms-form-builder') . '</th>';
        $profile_mapping_rows .= '</tr></thead><tbody>';
        foreach ($mapping_specs as $mapping_spec) {
            $profile_mapping_rows .= $this->renderAccountMappingRow(
                $mapping_spec,
                $rule,
                $field_options,
                $mapping_mode_options,
                $profile_image_mode_options,
                $is_iterable_email_field
            );
        }
        $profile_mapping_rows .= $this->renderMappingTableRow(
            __('Username', 'griffinforms-form-builder'),
            $this->renderFactoryControl('select', 'username_mode', (string) ($rule['username_mode'] ?? 'generate_only'), 'accounts', array(
                'label' => __('Username', 'griffinforms-form-builder'),
                'show_label' => false,
                'options' => array(
                    'generate_only' => __('Generate automatically', 'griffinforms-form-builder'),
                    'mapped_or_generate' => __('Use a username field', 'griffinforms-form-builder'),
                ),
                'input_classes' => 'form-select form-select-sm',
                'container_classes' => 'mb-0',
                'attributes' => array('data-accounts-field' => 'username_mode'),
            )),
            $this->renderFactoryControl('select', 'username_field_id', (string) ($rule['username_field_id'] ?? ''), 'accounts', array(
                'label' => __('Username Field', 'griffinforms-form-builder'),
                'show_label' => false,
                'options' => $text_field_select_options,
                'placeholder' => __('Select a field', 'griffinforms-form-builder'),
                'input_classes' => 'form-select form-select-sm',
                'container_classes' => 'mb-0',
                'attributes' => array(
                    'data-accounts-field' => 'username_field_id',
                    'data-accounts-visible-when' => 'username_mode:mapped_or_generate',
                ),
            ))
        );
        $profile_mapping_rows .= '</tbody></table></div>';

        $html .= $this->renderStageCard(
            'step_4',
            __('Profile details', 'griffinforms-form-builder'),
            __('Add these only if your form collects them. They are optional for user registration.', 'griffinforms-form-builder'),
            ($is_iterable_email_field
                ? '<div class="gf-notice gf-notice-warning mb-3" role="alert"><div><span class="fw-semibold">' . esc_html__('Multiple email values:', 'griffinforms-form-builder') . '</span> ' . esc_html__('This user registration can create more than one user from a single submission. First name, last name, and username mappings only work with iterable source fields. Profile image mapping is not available here.', 'griffinforms-form-builder') . '</div></div>'
                : '')
            . $profile_mapping_rows,
            false
        );

        $html .= $this->renderStageCard(
            'step_5',
            __('Delivery', 'griffinforms-form-builder'),
            __('Review the final sign-up and email choices for this user registration.', 'griffinforms-form-builder'),
            '<div class="gf-accounts-optional-section-title fw-semibold mb-2">' . esc_html__('If this email already exists', 'griffinforms-form-builder') . '</div>' .
            '<div class="text-muted small mb-3">' . esc_html__('Decide how user registration should behave if the submitted email already belongs to a user.', 'griffinforms-form-builder') . '</div>' .
            $this->renderFactoryControl('select', 'existing_account_behavior', (string) ($rule['existing_account_behavior'] ?? 'error'), 'accounts', array(
                'label' => __('Duplicate Email Handling', 'griffinforms-form-builder'),
                'show_label' => false,
                'options' => array(
                    'error' => __('Block this sign-up', 'griffinforms-form-builder'),
                    'update_user' => __('Update the existing user', 'griffinforms-form-builder'),
                    'accept_noop' => __('Keep the submission only', 'griffinforms-form-builder'),
                ),
                'input_classes' => 'form-select form-select-sm',
                'container_classes' => 'mb-3',
                'attributes' => array('data-accounts-field' => 'existing_account_behavior'),
            )) .
            '<div class="form-text mb-4">' .
                esc_html__('Block this sign-up stops the registration. Update the existing user applies the mapped changes to the user who already exists. Keep the submission only accepts the form without changing the existing user.') .
            '</div>' .
            '<div class="gf-accounts-optional-section-title fw-semibold mb-2">' . esc_html__('Email confirmation', 'griffinforms-form-builder') . '</div>' .
            $this->renderFactoryControl('checkbox', 'send_confirmation_email', !empty($rule['send_confirmation_email']), 'accounts', array(
                'checkbox_label' => __('Send a confirmation email', 'griffinforms-form-builder'),
                'container_classes' => 'mt-1 mb-3',
                'attributes' => array('data-accounts-field' => 'send_confirmation_email'),
            )) .
            '<div class="gf-accounts-confirmation-message-column" data-accounts-visible-when="send_confirmation_email:1">' .
            $this->renderFactoryControl('select', 'confirmation_message_id', (string) ($rule['confirmation_message_id'] ?? ''), 'accounts', array(
                'label' => __('Confirmation Email Message', 'griffinforms-form-builder'),
                'show_label' => false,
                'options' => $message_map,
                'placeholder' => __('Select a message', 'griffinforms-form-builder'),
                'input_classes' => 'form-select form-select-sm',
                'container_classes' => 'gf-accounts-confirmation-message-control mb-2',
                'attributes' => array(
                    'data-accounts-field' => 'confirmation_message_id',
                ),
            )) .
            '<div class="form-text gf-accounts-confirmation-timing-note" data-accounts-note="confirmation_timing">' .
                esc_html__('If this registration waits for approval or manual review, the confirmation email is sent after the admin finishes that step.') .
            '</div>' .
            '</div>',
            false
        );

        if (!$is_persisted_rule) {
            $html .= $this->renderStageCard(
                'step_6',
                __('Review and save', 'griffinforms-form-builder'),
                __('Review this user registration before saving it.', 'griffinforms-form-builder'),
                '<div class="gf-accounts-review-summary" data-accounts-review-summary>' . $this->renderAccountRuleOverview($rule, $field_options, $role_map, $message_map) . '</div>',
                false
            );
        }

        return $html;
    }

    protected function renderFactoryControl(string $type, string $key, $value, string $context, array $config): string
    {
        $control = ControlFactory::create($type, $key, $value, $context, $config);
        return $control->render();
    }

    protected function renderControlColumn(string $html, string $column_class = 'col-md-6', array $attributes = array()): string
    {
        $attribute_html = '';
        foreach ($attributes as $attribute_key => $attribute_value) {
            $attribute_html .= ' ' . esc_attr((string) $attribute_key) . '="' . esc_attr((string) $attribute_value) . '"';
        }

        return '<div class="' . esc_attr($column_class) . '"' . $attribute_html . '>' . $html . '</div>';
    }

    protected function renderMappingTableRow(string $field_label, string $strategy_control, string $field_control): string
    {
        $html  = '<tr>';
        $html .= '<td class="fw-medium align-middle">' . esc_html($field_label) . '</td>';
        $html .= '<td>' . $strategy_control . '</td>';
        $html .= '<td>' . $field_control . '</td>';
        $html .= '</tr>';

        return $html;
    }

    protected function renderAccountMappingRow(
        array $mapping_spec,
        array $rule,
        array $field_options,
        array $mapping_mode_options,
        array $profile_image_mode_options,
        bool $is_iterable_email_field
    ): string {
        $mode_key = $mapping_spec['mode_key'];
        $field_key = $mapping_spec['field_key'];
        $source_group = $mapping_spec['source_group'];
        $mode_options = ($mapping_spec['mode_options'] ?? '') === 'profile_image_mode_options'
            ? $profile_image_mode_options
            : $mapping_mode_options;
        $field_select_options = $this->mapOptionsForSelect(
            $field_options[$source_group] ?? array(),
            $is_iterable_email_field
        );

        return $this->renderMappingTableRow(
            $mapping_spec['label'],
            $this->renderFactoryControl('select', $mode_key, (string) ($rule[$mode_key] ?? 'none'), 'accounts', array(
                'label' => sprintf(__('%s Strategy', 'griffinforms-form-builder'), $mapping_spec['label']),
                'show_label' => false,
                'options' => $mode_options,
                'input_classes' => 'form-select form-select-sm',
                'container_classes' => 'mb-0',
                'attributes' => array('data-accounts-field' => $mode_key),
            )),
            $this->renderFactoryControl('select', $field_key, (string) ($rule[$field_key] ?? ''), 'accounts', array(
                'label' => sprintf(__('%s Field', 'griffinforms-form-builder'), $mapping_spec['label']),
                'show_label' => false,
                'options' => $field_select_options,
                'placeholder' => $mapping_spec['placeholder'],
                'input_classes' => 'form-select form-select-sm',
                'container_classes' => 'mb-0',
                'attributes' => array(
                    'data-accounts-field' => $field_key,
                    'data-accounts-visible-when' => $mode_key . ':map_field',
                ),
            ))
        );
    }

    protected function mapOptionsForSelect(array $options, bool $require_iterable = false): array
    {
        $mapped = array();
        foreach ($options as $option) {
            if (!is_array($option)) {
                continue;
            }
            if ($require_iterable && empty($option['is_iterable'])) {
                continue;
            }
            $mapped[(string) ($option['id'] ?? '')] = (string) ($option['label'] ?? '');
        }
        return $mapped;
    }

    protected function filterAvailableEmailFields(array $email_fields, array $rules, string $current_rule_id = ''): array
    {
        $used_email_field_ids = array();

        foreach ($rules as $rule) {
            if (!is_array($rule)) {
                continue;
            }

            if (($rule['id'] ?? '') === $current_rule_id) {
                continue;
            }

            $email_field_id = absint($rule['email_field_id'] ?? 0);
            if ($email_field_id > 0) {
                $used_email_field_ids[] = $email_field_id;
            }
        }

        if (empty($used_email_field_ids)) {
            return $email_fields;
        }

        return array_values(array_filter($email_fields, static function ($field) use ($used_email_field_ids) {
            if (!is_array($field)) {
                return false;
            }

            return !in_array(absint($field['id'] ?? 0), $used_email_field_ids, true);
        }));
    }

    protected function renderStageCard(string $stage, string $title, string $description, string $content, bool $is_open = false): string
    {
        $classes = 'griffinforms-accounts-stage border rounded p-3 mb-3';
        if (!$is_open) {
            $classes .= ' d-none';
        }

        $step_number = '';
        $step_title = $title;
        if (preg_match('/^Step\s+(\d+):\s*(.+)$/i', $title, $matches)) {
            $step_number = (string) ($matches[1] ?? '');
            $step_title = (string) ($matches[2] ?? $title);
        }

        $short_labels = array(
            'step_1' => __('Sign-up', 'griffinforms-form-builder'),
            'step_2' => __('Workflow', 'griffinforms-form-builder'),
            'step_3' => __('Access', 'griffinforms-form-builder'),
            'step_4' => __('Profile', 'griffinforms-form-builder'),
            'step_5' => __('Delivery', 'griffinforms-form-builder'),
            'step_6' => __('Review', 'griffinforms-form-builder'),
        );
        $short_label = $short_labels[$stage] ?? $step_title;

        $html  = '<div class="' . esc_attr($classes . ' gf-guided-flow-step') . '" data-accounts-stage="' . esc_attr($stage) . '" data-guided-flow-step="' . esc_attr($stage) . '" data-guided-flow-label="' . esc_attr($step_title) . '" data-guided-flow-short-label="' . esc_attr($short_label) . '">';
        $html .= '<div class="mb-3">';
        $html .= '<div class="d-flex align-items-start gap-3">';
        if ($step_number !== '') {
            $html .= '<span class="griffinforms-accounts-step-badge">' . esc_html($step_number) . '</span>';
        }
        $html .= '<div class="flex-grow-1">';
        $html .= '<div class="fw-semibold">' . esc_html($step_title) . '</div>';
        $html .= '<div class="text-muted small">' . esc_html($description) . '</div>';
        $html .= '</div>';
        $html .= '</div>';
        $html .= '</div>';
        $html .= $content;
        $html .= '</div>';

        return $html;
    }

    protected function renderAccountRuleOverview(array $rule, array $field_options, array $role_map, array $message_map): string
    {
        $email_label = $this->findOptionLabelById($field_options['email_fields'] ?? array(), (int) ($rule['email_field_id'] ?? 0), __('Choose an email field', 'griffinforms-form-builder'));
        $password_label = $this->getAccountAccessLabel((string) ($rule['password_mode'] ?? 'generate_password'));
        $workflow_label = $this->getRegistrationWorkflowLabel((string) ($rule['creation_mode'] ?? 'immediate_create'));
        $duplicate_label = $this->getDuplicateEmailLabel((string) ($rule['existing_account_behavior'] ?? 'error'));
        $role_label = $role_map[(string) ($rule['role'] ?? 'subscriber')] ?? __('Subscriber', 'griffinforms-form-builder');
        $confirmation_label = !empty($rule['send_confirmation_email'])
            ? (!empty($rule['confirmation_message_id']) ? __('On', 'griffinforms-form-builder') . ' · ' . ($message_map[(string) ($rule['confirmation_message_id'] ?? '')] ?? __('Choose a message', 'griffinforms-form-builder')) : __('On', 'griffinforms-form-builder'))
            : __('Off', 'griffinforms-form-builder');

        $mapped_details = array();
        if (($rule['first_name_mode'] ?? 'none') === 'map_field') {
            $mapped_details[] = __('First name', 'griffinforms-form-builder');
        }
        if (($rule['last_name_mode'] ?? 'none') === 'map_field') {
            $mapped_details[] = __('Last name', 'griffinforms-form-builder');
        }
        if (($rule['username_mode'] ?? 'generate_only') === 'mapped_or_generate') {
            $mapped_details[] = __('Username', 'griffinforms-form-builder');
        }
        if (($rule['profile_image_mode'] ?? 'none') === 'map_field') {
            $mapped_details[] = __('Profile image', 'griffinforms-form-builder');
        }

        $html  = '<div class="gf-accounts-overview-card border rounded p-3">';
        $html .= '<div class="gf-accounts-overview-title fw-semibold mb-3">' . esc_html__('User Registration Summary', 'griffinforms-form-builder') . '</div>';
        $html .= '<div class="row g-3">';
        $html .= $this->renderAccountOverviewItem(__('Email', 'griffinforms-form-builder'), $email_label);
        $html .= $this->renderAccountOverviewItem(__('Workflow', 'griffinforms-form-builder'), $workflow_label);
        $html .= $this->renderAccountOverviewItem(__('Role', 'griffinforms-form-builder'), $role_label);
        $html .= $this->renderAccountOverviewItem(__('Access', 'griffinforms-form-builder'), $password_label);
        $html .= $this->renderAccountOverviewItem(__('Duplicate email', 'griffinforms-form-builder'), $duplicate_label);
        $html .= $this->renderAccountOverviewItem(__('Confirmation email', 'griffinforms-form-builder'), $confirmation_label);
        if (!empty($mapped_details)) {
            $html .= $this->renderAccountOverviewItem(__('Profile details', 'griffinforms-form-builder'), implode(', ', $mapped_details));
        }
        $html .= '</div>';
        $html .= '</div>';

        return $html;
    }

    protected function renderAccountOverviewItem(string $label, string $value): string
    {
        return '<div class="col-md-6"><div class="small text-muted mb-1">' . esc_html($label) . '</div><div class="fw-medium">' . esc_html($value) . '</div></div>';
    }

    protected function getRegistrationWorkflowLabel(string $mode): string
    {
        $labels = array(
            'immediate_create' => __('Create automatically', 'griffinforms-form-builder'),
            'pending_admin_activation' => __('Approve before creating the user', 'griffinforms-form-builder'),
            'manual_review_only' => __('Review first, then decide', 'griffinforms-form-builder'),
        );

        return $labels[$mode] ?? $mode;
    }

    protected function getAccountAccessLabel(string $mode): string
    {
        $labels = array(
            'mapped_password' => __('Use a password field', 'griffinforms-form-builder'),
            'generate_password' => __('Send password setup email', 'griffinforms-form-builder'),
        );

        return $labels[$mode] ?? $mode;
    }

    protected function getDuplicateEmailLabel(string $mode): string
    {
        $labels = array(
            'error' => __('Block this sign-up', 'griffinforms-form-builder'),
            'update_user' => __('Update the existing user', 'griffinforms-form-builder'),
            'accept_noop' => __('Keep the submission only', 'griffinforms-form-builder'),
        );

        return $labels[$mode] ?? $mode;
    }
}
