var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
  return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { MapToComponentField, MapToComponentMethod, VegaSlimmer } from 'vega-slimmer/core';
import { tryGetDocument } from '../../../../utils/try-get-document';
import { VegaInternalFormSubmit } from '../../../../helpers/event-manager/event-id/vega-internal-event-id';
import changeManager from '../../../../helpers/change-manager/change-manager';
import domNodesSubjectFactory from '../../../../helpers/change-manager/subject/dom-node-subject/dom-node-subject-factory';
import { Observer } from '../../../../helpers/change-manager/observer/observer';
import { findParent } from '../../../../utils/ui';
import { isNonNullable } from '../../../../types/type-guard';
/**
 * Controller responsible for handling form submission in the VegaForm component.
 */
export class VegaFormSubmitController extends VegaSlimmer {
  /**
   * Component lifecycle - [connectedCallback]
   */
  connectedCallback() {
    this.registerFormSubmitChangeManager();
  }
  /**
   * Component lifecycle - [disconnectedCallback]
   */
  disconnectedCallback() {
    this.unregisterFormSubmitChangeManager();
  }
  /**
   * Submits the form using native HTML form submission with all current field values.
   *
   * This method collects all form field values, creates hidden input elements for each field,
   * appends them to a container inside the native form, and then triggers the native
   * `requestSubmit()` method to submit the form.
   *
   * The method ensures that the form data is available in the native form for submission,
   * which is useful when `useNativeForm` is enabled.
   *
   * @returns {Promise<void>} Resolves when the form submission process is complete.
   */
  async submitWithFormData() {
    if (!this.useNativeForm)
      return;
    const data = await this.host.getValue();
    for (const dataKey in data) {
      if (typeof data[dataKey] === 'object') {
        // Use bracket notation for nested form object values
        const bracketKeyMap = this.flattenObjectWithBrackets({
          [dataKey]: data[dataKey],
        });
        for (const bracketKey in bracketKeyMap) {
          const nestedHiddenInput = this.createHiddenInput(bracketKey, bracketKeyMap[bracketKey]);
          this.nativeFormRenderer.getNativeFormRef().append(nestedHiddenInput);
        }
      }
      else {
        const hiddenInput = this.createHiddenInput(dataKey, data[dataKey]);
        this.nativeFormRenderer.getNativeFormRef().append(hiddenInput);
      }
    }
    this.nativeFormRenderer.getNativeFormRef().requestSubmit();
  }
  /**
   * Creates a hidden input element with the specified name and value.
   *
   * @param {string} name - The name attribute for the hidden input.
   * @param {unknown} value - The value attribute for the hidden input.
   * @returns {HTMLInputElement} The created hidden input element.
   */
  createHiddenInput(name, value) {
    const hiddenInput = tryGetDocument().createElement('input');
    hiddenInput.type = 'hidden';
    hiddenInput.name = name;
    hiddenInput.value = isNonNullable(value) ? String(value) : '';
    return hiddenInput;
  }
  /**
   * Registers the form submit event observer with the ChangeManager.
   */
  registerFormSubmitChangeManager() {
    var _a;
    this.formSubmitEventObserver = new Observer(this.canAcceptFormSubmitEvent.bind(this), this.handleSubmitEvents.bind(this));
    const outerForm = (_a = findParent(this.host, 'vega-form', { outerMost: true })) !== null && _a !== void 0 ? _a : this.host;
    changeManager.register(domNodesSubjectFactory.getSubject(outerForm, VegaInternalFormSubmit), this.formSubmitEventObserver);
  }
  /**
   * Unregister the form submit event observer from the ChangeManager.
   */
  unregisterFormSubmitChangeManager() {
    var _a;
    const outerForm = (_a = findParent(this.host, 'vega-form', { outerMost: true })) !== null && _a !== void 0 ? _a : this.host;
    changeManager.unregister(domNodesSubjectFactory.getSubject(outerForm, VegaInternalFormSubmit), this.formSubmitEventObserver);
  }
  /*
   * The below method is e2e-test covered in
   * @see{module:vega-form-field-enter-to-submit-e2e}
   */
  /* istanbul ignore next */
  /**
   * Can accept handler for FORM_SUBMIT.
   *
   * @param {FormEventsPayload} payload - The payload containing the host element that triggered the submit event.
   * @returns {boolean} True if the host element is part of the current form, false otherwise.
   */
  canAcceptFormSubmitEvent(payload) {
    return this.vegaFormFieldController.isElementInCurrentForm(payload.host);
  }
  /**
   * Handles the form submission process.
   *
   * @returns {Promise<void>} Resolves when the submission process is complete.
   */
  async handleSubmitEvents() {
    const valid = await this.host.valid();
    if (valid.isValid) {
      const formValue = await this.host.getValue();
      const submitEvent = this.submitEventEmitter.emit(formValue);
      if (!submitEvent.defaultPrevented) {
        await this.submitWithFormData();
      }
    }
  }
  /**
   * Flattens a nested object into a single-level object with bracket notation for keys.
   *
   * @param {Record<string, unknown>} data - The nested object to be flattened.
   * @param {string} [parentKey=''] - The prefix for the keys in the flattened object.
   * @param {Record<string, unknown>} [out={}] - The output object that accumulates the flattened key-value pairs.
   * @returns {Record<string, unknown>} The flattened object with bracket notation keys.
   */
  flattenObjectWithBrackets(data, parentKey = '', out = {}) {
    Object.entries(data).forEach(([key, value]) => {
      const currentKey = parentKey ? `${parentKey}[${key}]` : key;
      if (Array.isArray(value)) {
        // Recursively handle array items using index as key
        value.forEach((item, index) => {
          const arrayKey = `${currentKey}[${index}]`;
          if (typeof item === 'object' && item !== null) {
            // Recursively flatten objects inside the array
            this.flattenObjectWithBrackets(item, arrayKey, out);
          }
          else {
            out[arrayKey] = item;
          }
        });
      }
      else if (isNonNullable(value) && typeof value === 'object') {
        this.flattenObjectWithBrackets(value, currentKey, out);
      }
      else {
        out[currentKey] = value;
      }
    });
    return out;
  }
}
__decorate([
  MapToComponentField()
], VegaFormSubmitController.prototype, "host", void 0);
__decorate([
  MapToComponentField()
], VegaFormSubmitController.prototype, "useNativeForm", void 0);
__decorate([
  MapToComponentField()
], VegaFormSubmitController.prototype, "nativeFormRenderer", void 0);
__decorate([
  MapToComponentField()
], VegaFormSubmitController.prototype, "submitEventEmitter", void 0);
__decorate([
  MapToComponentField()
], VegaFormSubmitController.prototype, "vegaFormFieldController", void 0);
__decorate([
  MapToComponentMethod('connectedCallback')
], VegaFormSubmitController.prototype, "connectedCallback", null);
__decorate([
  MapToComponentMethod('disconnectedCallback')
], VegaFormSubmitController.prototype, "disconnectedCallback", null);
