import React, { ElementType, forwardRef } from 'react';
import { parse, HTMLElement, NodeType } from 'node-html-parser';
import { motion, MotionProps, Variants } from 'framer-motion';
import TextAnimated from '../TextAnimated';

export interface BoldTitleAnimatedProps {
    html?: string | null;
    as?: ElementType;
    className?: string;
    classNameLetter?: string | (({ type, text, bold }: { type: 'text'; text: string; bold: boolean }) => string);
    classNameWord?: string;
    variantsTitle?: Variants;
    variantsLine?: Variants;
    variantsLetter?: Variants;
    motion?: MotionProps;
}

export type Line =
    | {
          type: 'break';
      }
    | { type: 'text'; text: string; bold: boolean };

const reduceParsed = (html: HTMLElement, bold?: boolean): Line[] => {
    return (html.childNodes as HTMLElement[]).reduce((a, c) => {
        if (c.nodeType === NodeType.TEXT_NODE) {
            return [...a, { type: 'text', text: c.text, bold: !!bold }];
        }

        if (c.nodeType === NodeType.ELEMENT_NODE && c.tagName === 'BR') {
            return [...a, { type: 'break' }];
        }

        if (c.nodeType === NodeType.ELEMENT_NODE && c.childNodes && (c.tagName === 'B' || c.tagName === 'STRONG')) {
            return [...a, ...reduceParsed(c, true)];
        }

        if (c.nodeType === NodeType.ELEMENT_NODE && c.childNodes && c.childNodes.length > 0) {
            return [...a, ...reduceParsed(c)];
        }

        return a;
    }, [] as Line[]);
};

const BoldTitleAnimated = forwardRef<HTMLElement, BoldTitleAnimatedProps>(
    (
        {
            as = 'h3',
            html,
            className,
            classNameLetter,
            classNameWord,
            variantsTitle = {
                initial: {},
                animate: { transition: { staggerChildren: 0.3 } }
            },
            variantsLine = {
                initial: {},
                animate: { transition: { staggerChildren: 0.025 } }
            },
            variantsLetter = {
                initial: {
                    y: '100%',
                    opacity: 0,
                    transition: { ease: [0.455, 0.03, 0.515, 0.955], duration: 0.85 }
                },
                animate: {
                    y: 0,
                    opacity: 1,
                    transition: { ease: [0.455, 0.03, 0.515, 0.955], duration: 0.3 }
                }
            },
            motion: animation,
            ...props
        },
        ref
    ) => {
        if (!html) {
            return null;
        }

        const Tag = motion(as);
        const lines: Line[][] = reduceParsed(parse(html)).reduce(
            (a, c) => {
                const last = a.length - 1;

                if (c.type === 'break') {
                    a.push([]);
                } else {
                    a[last] = [...a[last], c];
                }

                return a;
            },
            [[]] as Line[][]
        );

        return (
            <Tag ref={ref} className={className} variants={variantsTitle} animate="animate" initial="initial" {...props} {...animation}>
                {lines.map((line, index) => (
                    <TextAnimated
                        key={index}
                        classNameLetter={classNameLetter}
                        classNameWord={classNameWord}
                        className="block"
                        as="span"
                        variantsLetter={variantsLetter}
                        variantsLine={variantsLine}
                        line={line}
                    />
                ))}
            </Tag>
        );
    }
);

export default BoldTitleAnimated;
