import { useRef } from "react";
import clsx from "clsx";
import { m, useInView, type Variants } from "framer-motion";

import DynamicComponent from "@/components/dynamic-component";
import { staggeredSlideUpVariant } from "@/helpers/animation";
import {
  extractTextFromReactNode,
  UNICODE_NO_BREAK_SPACE,
  widont,
} from "@/helpers/string";

import styles from "./styles.module.scss";

interface SplitTextProps {
  text?: string;
  as?: "p" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "span" | "code";
  splitBy?: "word" | "char";
  delimiter?: string;
  variants?: Variants;
  animate?: boolean;
  triggerOnce?: boolean;
  children?: React.ReactNode;
  className?: string;
}

const SplitText = ({
  text,
  as = "p",
  splitBy = "word",
  delimiter,
  variants = staggeredSlideUpVariant(0.01),
  animate,
  triggerOnce = false,
  children,
  className,
  ...props
}: SplitTextProps) => {
  const ref = useRef(null);
  const inView = useInView(ref, {
    once: triggerOnce,
    amount: 0.1,
  });

  if (!text && !children) return null;

  const content = text ?? extractTextFromReactNode(children) ?? "";
  const balancedChildren = widont(content);

  // Include spaces in the regex pattern to preserve conventional text structures
  let splitPattern;
  if (delimiter) {
    // Include delimiter with the chunk it delimits
    splitPattern = new RegExp(
      `(.*?${delimiter})\\s*|(.+?\\s)(?=.*${delimiter}|$)`,
      "g",
    );
  } else if (splitBy === "char") {
    splitPattern = /(\s?)/;
  } else {
    splitPattern = /(\s+)/;
  }

  const splitText = balancedChildren
    .split(splitPattern)
    .filter(Boolean)
    .map((s) => s.replace(/\s/g, UNICODE_NO_BREAK_SPACE));

  return (
    <DynamicComponent
      ref={ref}
      tag={as}
      className={clsx(styles.splitText, className)}
    >
      {splitText.map((part, index) => (
        <span key={part + index} className={styles.outer}>
          <m.span
            className={styles.inner}
            initial="initial"
            animate={animate ?? inView ? "animate" : "initial"}
            custom={index}
            variants={variants}
            {...props}
          >
            {part}
          </m.span>
        </span>
      ))}
    </DynamicComponent>
  );
};

export default SplitText;
