'use strict';

const globalSlimmerRegistry = require('./global-slimmer-registry-b3bce7e0.js');
const mutationObserverSlimmer = require('./mutation-observer-slimmer-749d5020.js');

var __decorate = (undefined && undefined.__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;
};
/**
 * Maps component attributes/properties to aria-XXX attributes on an inner native element.
 * Observes attribute changes on host and updates the target element accordingly.
 */
class AriaAttributesValueMapper extends mutationObserverSlimmer.MutationObserverSlimmer {
  /**
   * The constructor initializes a class instance with a targetGetter function and an ariaMap.
   *
   * @param {() => HTMLElement} targetGetter - a function that returns the target element to set aria attributes on
   * @param {AriaAttributesMap} ariaMap - a map where the key is the aria attribute to set on the target element, and the value is an object that defines the dependencies (attributes/properties on host to check for value) and an optional default value
   */
  constructor(targetGetter, ariaMap) {
    // Build attributeFilter from dependencies
    const attrs = Array.from(new Set(Object.values(ariaMap).reduce((acc, cur) => acc.concat(cur.dependency), [])));
    // create entry for MutationObserverSlimmer to observe host attributes (omit target so host is observed)
    super({
      options: { attributes: true, attributeFilter: attrs },
      // eslint-disable-next-line jsdoc/require-jsdoc
      callback: (mutations) => {
        this.handleMutations(mutations);
      },
    });
    this.targetGetter = targetGetter;
    this.ariaMap = ariaMap;
  }
  /**
   * Component lifecycle - [componentDidLoad]
   */
  componentDidLoad() {
    this.performMapping();
    super.componentDidLoad && super.componentDidLoad();
  }
  /**
   * Run mapping after each component render to ensure refs assigned and
   * attributes/properties are up-to-date. This guarantees the mapper runs
   * even when attribute changes are observed before the inner target ref
   * is available.
   */
  componentDidRender() {
    this.performMapping();
  }
  /**
   * The `handleMutations` function processes an array of `MutationRecord` objects to determine which attributes have changed and then calls the `performMapping` function to update the corresponding aria attributes on the target element. It collects the names of the changed attributes into a set and passes it to the `performMapping` function, which uses this information to decide which aria attributes need to be updated based on their defined dependencies.
   *
   * @param {MutationRecord[]} mutations - an array of MutationRecord objects that represent the changes observed by the MutationObserver. Each MutationRecord contains information about the type of mutation, the target element that was mutated, and any relevant attribute names if the mutation was an attribute change.
   */
  handleMutations(mutations) {
    const changedAttrs = new Set();
    if (mutations && mutations.length) {
      for (const m of mutations) {
        if (m.attributeName)
          changedAttrs.add(m.attributeName);
      }
    }
    this.performMapping(changedAttrs.size ? changedAttrs : undefined);
  }
  /**
   * The `performMapping` function updates the aria attributes on the target element based on the defined dependencies in the `ariaMap`. It checks which attributes have changed (if provided) and only updates the aria attributes that depend on those changed attributes. For each aria attribute defined in the `ariaMap`, it looks for the first non-empty value among its dependencies (checking both attributes and properties on the host element) and sets that as the value of the aria attribute on the target element. If all dependencies are empty or undefined, it uses the default value specified in the `ariaMap`. The function also includes error handling when setting attributes on the target element.
   *
   * @param {Set<string>} changedAttrs - a set of attribute names that have changed on the host element
   */
  performMapping(changedAttrs) {
    const target = this.targetGetter ? this.targetGetter() : null;
    if (!target)
      return;
    const host = this.host;
    Object.keys(this.ariaMap).forEach((ariaAttr) => {
      const { dependency, defaultValue } = this.ariaMap[ariaAttr];
      // If no relevant dependency changed, skip updating this ariaAttr
      if (changedAttrs && !dependency.some((item) => changedAttrs.has(item))) {
        return;
      }
      let finalValue = null;
      for (const item of dependency) {
        // Try attribute first
        let val = host.getAttribute(item);
        if (val === null || val === undefined || val === '') {
          // Try property on host if attribute not present or empty
          // support dashed-name -> camelCase property (e.g. data-aria-label -> dataAriaLabel)
          const camel = item.replace(/-([a-z])/g, (_m, p1) => p1.toUpperCase());
          const p1 = host[item];
          const p2 = host[camel];
          if (p1 !== undefined && p1 !== null) {
            val = String(p1);
          }
          else if (p2 !== undefined && p2 !== null) {
            val = String(p2);
          }
        }
        if (val !== null && val !== undefined && String(val).trim() !== '') {
          finalValue = val;
          break;
        }
      }
      if (finalValue === null || finalValue === undefined || String(finalValue).trim() === '') {
        finalValue = defaultValue;
      }
      try {
        if (String(finalValue).trim() === '') {
          target.removeAttribute(ariaAttr);
          return;
        }
        const existing = target.getAttribute(ariaAttr);
        if (finalValue === null || finalValue === undefined) {
          if (existing !== null) {
            target.removeAttribute(ariaAttr);
          }
          return;
        }
        const stringValue = String(finalValue);
        if (existing !== stringValue) {
          target.setAttribute(ariaAttr, stringValue);
        }
      }
      catch (e) {
        globalSlimmerRegistry.LogUtility.warn('Errors occurred when setting aria attribute:', {
          ariaAttr,
          finalValue,
          error: e,
        });
      }
    });
  }
}
__decorate([
  globalSlimmerRegistry.MapToComponentMethod('componentDidLoad')
], AriaAttributesValueMapper.prototype, "componentDidLoad", null);
__decorate([
  globalSlimmerRegistry.MapToComponentMethod('componentDidRender')
], AriaAttributesValueMapper.prototype, "componentDidRender", null);

exports.AriaAttributesValueMapper = AriaAttributesValueMapper;
