<script lang="ts">
  /* eslint svelte/no-at-html-tags: "off" */
  import type { Component } from 'svelte';

  interface Rule {
    split: RegExp;
    match: RegExp;
    component: Component<any>;
    props?: Record<string, unknown>;
  }

  interface Props {
    html: string;
    rules?: Rule[];
  }

  let { html, rules = [] }: Props = $props();

  const partitionHtml = (html: string, rules: Rule[]) => {
    return rules.reduce(
      (acc, rule) => {
        return acc.flatMap((substr) => substr.split(rule.split));
      },
      [html]
    );
  };

  const findAndTransform = (
    rules: Rule[],
    predicate: (rule: Rule) => boolean,
    transform: (rule: Rule) => Rule
  ) => {
    const foundElement = rules.find(predicate);
    return foundElement ? transform(foundElement) : null;
  };
</script>

{#each partitionHtml(html, rules) as part}
  {@const match = findAndTransform(
    rules,
    (rule) => rule.split.test(part),
    (rule) => ({
      ...rule,
      props: { ...rule.props, matches: [...(part.match(rule.match) ?? [])], html }
    })
  )}

  {#if match}
    <match.component {...match.props} />
  {:else}
    {@html part}
  {/if}
{/each}
