import createElement from './createElement';
import defaultAttributes from './defaultAttributes';
import { Icons, SVGProps } from './types';
import { hasA11yProp, mergeClasses, toPascalCase } from '@lucide/shared';

export type CustomAttrs = { [attr: string]: unknown };

/**
 * Get the attributes of an HTML element.
 * @param {HTMLElement} element
 * @returns {Object}
 */
export const getAttrs = (element: Element): Record<string, string> =>
  Array.from(element.attributes).reduce<Record<string, string>>((attrs, attr) => {
    attrs[attr.name] = attr.value;
    return attrs;
  }, {});

/**
 * Gets the classNames of an attributes Object.
 * @param {Object} attrs
 * @returns {Array}
 */
export const getClassNames = (
  attrs: Record<string, number | string | string[]> | string,
): string | string[] => {
  if (typeof attrs === 'string') return attrs;
  if (!attrs || !attrs.class) return '';
  if (attrs.class && typeof attrs.class === 'string') {
    return attrs.class.split(' ');
  }
  if (attrs.class && Array.isArray(attrs.class)) {
    return attrs.class;
  }
  return '';
};

interface ReplaceElementOptions {
  nameAttr: string;
  icons: Icons;
  attrs: SVGProps;
}

/**
 * ReplaceElement, replaces the given element with the created icon.
 * @param {HTMLElement} element
 * @param {{ nameAttr: string, icons: object, attrs: object }} options: { nameAttr, icons, attrs }
 * @returns {Function}
 */
const replaceElement = (element: Element, { nameAttr, icons, attrs }: ReplaceElementOptions) => {
  const iconName = element.getAttribute(nameAttr);

  if (iconName == null) return;

  const ComponentName = toPascalCase(iconName);

  const iconNode = icons[ComponentName];

  if (!iconNode) {
    return console.warn(
      `${element.outerHTML} icon name was not found in the provided icons object.`,
    );
  }

  const elementAttrs = getAttrs(element);

  const ariaProps = hasA11yProp(elementAttrs) ? {} : { 'aria-hidden': 'true' };

  const iconAttrs = {
    ...defaultAttributes,
    'data-lucide': iconName,
    ...ariaProps,
    ...attrs,
    ...elementAttrs,
  } satisfies SVGProps;

  const elementClassNames = getClassNames(elementAttrs);
  const className = getClassNames(attrs);

  const classNames = mergeClasses(
    'lucide',
    `lucide-${iconName}`,
    ...elementClassNames,
    ...className,
  );

  if (classNames) {
    Object.assign(iconAttrs, {
      class: classNames,
    });
  }

  const svgElement = createElement(iconNode, iconAttrs);

  return element.parentNode?.replaceChild(svgElement, element);
};

export default replaceElement;
