import Head from 'next/head';
import { useState, useMemo, useRef, useCallback, useEffect, forwardRef, CSSProperties } from 'react';
import dynamic from 'next/dynamic';

const Loading = () => <p>Loading...</p>;

const JoditEditor = dynamic(() => import('jodit-react'), {
    ssr: false,
    loading: Loading,
});

const stripHtml = (value = '') =>
    value
        .replace(/<[^>]*>/g, ' ')
        .replace(/&nbsp;/gi, ' ')
        .trim();

const removeBackgroundStyles = (editor: any) => {
    if (!editor) return;
    const editable = editor?.editor;
    if (!editable) return;

    const elements = editable.querySelectorAll('[style]');
    elements.forEach((el: any) => {
        const currentStyle = el.getAttribute('style');
        if (!currentStyle) return;

        const filtered = currentStyle
            .split(';')
            .map((rule: any) => rule.trim())
            .filter((rule: any) => {
                if (!rule) return false;
                const [prop] = rule.split(':');
                if (!prop) return false;
                const propName = prop.trim().toLowerCase();
                if (propName.startsWith('background')) return false;
                return true;
            })
            .join('; ');

        if (filtered) {
            el.setAttribute('style', filtered);
        } else {
            el.removeAttribute('style');
        }
    });
};

const preventBackgroundCommand = (editor: any) => {
    if (!editor) return;

    if (!editor.__originalExecCommand) {
        const originalExec = editor.execCommand?.bind(editor);
        editor.__originalExecCommand = originalExec;

        if (originalExec) {
            editor.execCommand = (command: any, second: any, value: any) => {
                if (command === 'background') {
                    if (editor.__isPreventingBackground) {
                        return undefined;
                    }

                    editor.__isPreventingBackground = true;
                    const result = originalExec('background', false, '');
                    editor.__isPreventingBackground = false;
                    return result;
                }

                return originalExec(command, second, value);
            };
        }
    }
};

const sanitizeHtmlContent = (html = '') => {
    if (!html) return '';

    const fallbackClean = (raw: any) =>
        raw
            .replace(/background(?:-color)?\s*:\s*[^;"]*;?/gi, '')
            .replace(/\s?class="[^"]*"/gi, '')
            .replace(/\s?bgcolor="[^"]*"/gi, '')
            .replace(/\s?id="[^"]*"/gi, '');

    try {
        if (typeof window === 'undefined' || typeof window.DOMParser === 'undefined') {
            return fallbackClean(html);
        }

        const parser = new window.DOMParser();
        const doc = parser.parseFromString(`<div>${html}</div>`, 'text/html');
        const wrapper = doc.body.firstElementChild;

        if (!wrapper) {
            return '';
        }

        const allowedStyleProps = new Set(['color', 'font-size', 'font-family', 'font-weight', 'font-style', 'text-align', 'text-decoration', 'text-transform', 'line-height']);

        const cleanNode = (node: any) => {
            if (node.nodeType === 1) {
                Array.from(node.attributes).forEach((attr: any) => {
                    const name = attr.name.toLowerCase();
                    if (name === 'style') {
                        const currentStyle = attr.value;
                        const filteredStyle = currentStyle
                            .split(';')
                            .map((rule: any) => rule.trim())
                            .filter((rule: any) => {
                                if (!rule) return false;
                                const [prop] = rule.split(':');
                                if (!prop) return false;
                                const propName = prop.trim().toLowerCase();
                                if (propName.startsWith('background')) return false;
                                if (propName.startsWith('margin')) return false;
                                if (propName === 'border' || propName.startsWith('border-')) return false;
                                return allowedStyleProps.has(propName);
                            })
                            .join('; ');

                        if (filteredStyle) {
                            node.setAttribute('style', filteredStyle);
                        } else {
                            node.removeAttribute('style');
                        }
                    } else if (['class', 'id', 'bgcolor'].includes(name)) {
                        node.removeAttribute(attr.name);
                    }
                });
            }

            Array.from(node.childNodes).forEach((child: any) => {
                if (child.nodeType === 1) {
                    cleanNode(child);
                }
            });
        };

        cleanNode(wrapper);

        return wrapper.innerHTML;
    } catch (error) {
        return fallbackClean(html);
    }
};

const countWords = (value = '') => {
    const text = stripHtml(value);
    if (!text) return 0;
    return text.split(/\s+/).filter(Boolean).length;
};

const TITLE_CHAR_LIMIT = 30;

const cardConfig = [
    {
        id: "Card 01",
        image:
            "https://res.cloudinary.com/aurumlab/image/upload/f_auto,q_auto,c_limit/v1762754636/aurumlab/product/images/GIFT_CARD-01_bd5za0.png",
        font: "Freeman",
        titleFontSize: "50px",
        width: "100%",
        titleMarginTop: "25%",
        titleMarginBottom: "50px",
        titlePaddingTop: "20px",
        titlePaddingBottom: "20px",
        titlePaddingLeft: "50px",
        titlePaddingRight: "50px",
        contentFontSize: "20px",
        contentMarginTop: "20px",
        contentPaddingTop: "0px",
        contentPaddingLeft: "50px",
        contentPaddingRight: "50px",
        titleHeight: "120px",
    },
    {
        id: "Card 02",
        image:
            "https://res.cloudinary.com/aurumlab/image/upload/f_auto,q_auto,c_limit/v1762754636/aurumlab/product/images/GIFT_CARD-04_ce613r.png",
        font: "Freeman",
        titleFontSize: "55px",
        width: "100%",
        titleMarginTop: "20%",
        titleMarginBottom: "50px",
        titlePaddingTop: "20px",
        titlePaddingBottom: "20px",
        titlePaddingLeft: "40px",
        titlePaddingRight: "40px",
        contentFontSize: "20px",
        contentMarginTop: "20px",
        contentPaddingTop: "0px",
        contentPaddingLeft: "45px",
        contentPaddingRight: "45px",
        titleHeight: "120px",
    },
    {
        id: "Card 03",
        image:
            "https://res.cloudinary.com/aurumlab/image/upload/f_auto,q_auto,c_limit/v1762754637/aurumlab/product/images/GIFT_CARD-02_jsotdp.png",
        font: "Palmore",
        titleFontSize: "90px",
        width: "100%",
        titleMarginTop: "25%",
        titleMarginBottom: "30px",
        titlePaddingTop: "0px",
        titlePaddingBottom: "10px",
        titlePaddingLeft: "30px",
        titlePaddingRight: "30px",
        titleLineHeight: "0.8",
        contentFontSize: "21px",
        contentMarginTop: "0px",
        contentPaddingTop: "20px",
        contentPaddingLeft: "35px",
        contentPaddingRight: "35px",
        titleHeight: "120px",
    },
    {
        id: "Card 04",
        image:
            "https://res.cloudinary.com/aurumlab/image/upload/f_auto,q_auto,c_limit/v1762754636/aurumlab/product/images/GIFT_CARD-06_c7vcre.png",
        font: "AlbraDisplay",
        titleFontSize: "50px",
        width: "100%",
        titleMarginTop: "25%",
        titleMarginBottom: "50px",
        titlePaddingTop: "10px",
        titlePaddingBottom: "20px",
        titlePaddingLeft: "40px",
        titlePaddingRight: "40px",
        contentFontSize: "20px",
        contentMarginTop: "0px",
        contentPaddingTop: "0px",
        contentPaddingLeft: "45px",
        contentPaddingRight: "45px",
        titleHeight: "0px",
    },
    {
        id: "Card 05",
        image:
            "https://res.cloudinary.com/aurumlab/image/upload/f_auto,q_auto,c_limit/v1762754636/aurumlab/product/images/GIFT_CARD-07_tmpvdl.png",
        font: "AlbraDisplay",
        titleFontSize: "50px",
        width: "100%",
        titleMarginTop: "30%",
        titleMarginBottom: "30px",
        titlePaddingTop: "20px",
        titlePaddingBottom: "20px",
        titlePaddingLeft: "20px",
        titlePaddingRight: "20px",
        contentFontSize: "20px",
        contentMarginTop: "20px",
        contentPaddingTop: "0px",
        contentPaddingLeft: "50px",
        contentPaddingRight: "50px",
        titleHeight: "0px",
    },
    {
        id: "Card 06",
        image:
            "https://res.cloudinary.com/aurumlab/image/upload/f_auto,q_auto,c_limit/v1762754637/aurumlab/product/images/GIFT_CARD-03_mgroxq.png",
        font: "Freshgrape",
        titleFontSize: "50px",
        width: "100%",
        titleMarginTop: "20%",
        titleMarginBottom: "50px",
        titlePaddingTop: "20px",
        titlePaddingBottom: "20px",
        titlePaddingLeft: "15px",
        titlePaddingRight: "15px",
        contentFontSize: "20px",
        contentMarginTop: "20px",
        contentPaddingTop: "0px",
        contentPaddingLeft: "35px",
        contentPaddingRight: "35px",
    },
    {
        id: "Card 07",
        image:
            "https://res.cloudinary.com/aurumlab/image/upload/f_auto,q_auto,c_limit/v1762754636/aurumlab/product/images/GIFT_CARD-05_x5uv1u.png",
        font: "Freshgrape",
        titleFontSize: "65px",
        width: "100%",
        titleMarginTop: "20%",
        titleMarginBottom: "30px",
        titlePaddingTop: "20px",
        titlePaddingBottom: "20px",
        titlePaddingLeft: "15px",
        titlePaddingRight: "15px",
        contentFontSize: "24px",
        contentMarginTop: "20px",
        contentPaddingTop: "0px",
        contentPaddingLeft: "35px",
        contentPaddingRight: "35px",
        titleHeight: "120px",
    },
];


type GiftCardDisplayProps = {
    previewHeader: string;
    previewContent: string;
    ImageVariation?: string;
    ImageValue?: string;
};

const wait = (ms: number) =>
    new Promise((resolve) => {
        setTimeout(resolve, ms);
    });

const GiftCardDisplay = forwardRef<HTMLDivElement, GiftCardDisplayProps>(({ previewHeader, previewContent, ImageVariation, ImageValue }, ref) => {
    const selectedCard = useMemo(() => cardConfig.find((card: any) => card.id === ImageValue), [ImageValue]);

    const previewImageSrc = useMemo(() => {
        if (ImageValue && selectedCard?.image) {
            return selectedCard.image;
        }
        if (ImageVariation) {
            return ImageVariation;
        }
        return cardConfig[0]?.image || '';
    }, [ImageValue, ImageVariation, selectedCard]);

    const titleStyle = useMemo<CSSProperties>(
        () => ({
            fontFamily: selectedCard?.font || 'inherit',
            fontSize: selectedCard?.titleFontSize || '48px',
            textTransform: 'uppercase',
            textAlign: 'center',
            paddingLeft: selectedCard?.titlePaddingLeft || '0px',
            paddingRight: selectedCard?.titlePaddingRight || '0px',
            paddingTop: selectedCard?.titlePaddingTop || '0px',
            paddingBottom: selectedCard?.titlePaddingBottom || '0px',
            width: selectedCard?.width || '100%',
            marginTop: selectedCard?.titleMarginTop || '0px',
            marginBottom: selectedCard?.titleMarginBottom || '0px',
            lineHeight: selectedCard?.titleLineHeight || "1",
        }),
        [selectedCard]
    );


    const contentStyle = useMemo<CSSProperties>(
        () => ({
            fontFamily: 'NeuzeitGrotesk-Reg',
            textAlign: 'center',
            paddingLeft: selectedCard?.contentPaddingLeft || '0px',
            paddingRight: selectedCard?.contentPaddingRight || '0px',
            paddingTop: selectedCard?.contentPaddingTop || '0px',
            width: selectedCard?.width || '100%',
            fontSize: selectedCard?.contentFontSize || '18px',
        }),
        [selectedCard]
    );

    return (
        <div ref={ref} className="flex flex-1 items-center justify-center p-[0px] shadow-xl">
            <div className="relative flex items-center justify-center">
                <img src={previewImageSrc} alt="Gift Card" className="mx-auto w-full object-cover" />
                <div className="absolute left-0 top-0 flex h-full w-full items-start justify-center">
                    <div className="relative flex flex-col items-center">
                        <div
                            className="text-center"
                            style={titleStyle}
                            dangerouslySetInnerHTML={{
                                __html: previewHeader || '<p style="color:#999;text-transform:uppercase;text-align:center;">TITLE HERE</p>',
                            }}
                        />
                        <div className=" items-start justify-center mt-[20px]">
                            <div
                                style={{ ...contentStyle }}
                                dangerouslySetInnerHTML={{
                                    __html: previewContent || '<p style="color:#999;text-align:center;">Your message will appear here</p>',
                                }}
                            />
                        </div>
                    </div>
                </div>
            </div>
        </div>
    );
});
GiftCardDisplay.displayName = 'GiftCardDisplay';

const GenerateGiftCard = () => {
    const [title, setTitle] = useState('');
    const [description, setDescription] = useState('');
    const [selectedCard, setSelectedCard] = useState('Card 01');
    const [isGeneratingPDF, setIsGeneratingPDF] = useState(false);
    const titleUpdateSourceRef = useRef<string | null>(null);
    const descriptionUpdateSourceRef = useRef<string | null>(null);
    const titleValueRef = useRef(title);
    const previewRef = useRef<HTMLDivElement | null>(null);

    useEffect(() => {
        titleValueRef.current = title;
    }, [title]);

    const applyPasteLimiter = useCallback((editor: any, limit: any, valueRef: any) => {
        if (!editor || !editor.events) return;

        if (!editor.__pasteLimiters) {
            editor.__pasteLimiters = {};
        }

        const handlerKey = `limit_${limit}`;

        const handler = (event: any) => {
            if (!event) return;
            const clipboard = event.clipboardData || (window as any).clipboardData;
            if (!clipboard) return;

            let text = clipboard.getData('text');
            if (!text) return;

            const getSelectedPlainLength = () => {
                if (!editor.selection) return 0;
                try {
                    if (typeof editor.selection.getText === 'function') {
                        const selectedText = editor.selection.getText();
                        return selectedText ? stripHtml(selectedText).length : 0;
                    }
                    if (typeof editor.selection.getHTML === 'function') {
                        const selectedHtml = editor.selection.getHTML();
                        return selectedHtml ? stripHtml(selectedHtml).length : 0;
                    }
                } catch (error) {
                    return 0;
                }
                return 0;
            };

            const currentPlainLength = stripHtml(valueRef.current || '').length;
            const selectedPlainLength = getSelectedPlainLength();
            const baseLength = Math.max(currentPlainLength - selectedPlainLength, 0);
            const available = limit - baseLength;

            if (available <= 0) {
                event.preventDefault();
                return;
            }

            if (text.length > available) {
                event.preventDefault();
                const truncatedText = text.slice(0, available);
                const htmlToInsert = truncatedText.replace(/\n/g, '<br>');
                if (editor.selection && typeof editor.selection.insertHTML === 'function') {
                    editor.selection.insertHTML(htmlToInsert);
                }
            }
        };

        const previousHandler = editor.__pasteLimiters[handlerKey];
        if (previousHandler) {
            editor.events.off('beforePaste', previousHandler);
        }

        editor.__pasteLimiters[handlerKey] = handler;
        editor.events.on('beforePaste', handler);
    }, []);

    const titleStats = useMemo(
        () => ({
            words: countWords(title),
            chars: stripHtml(title).length,
        }),
        [title]
    );

    const descriptionStats = useMemo(
        () => ({
            words: countWords(description),
            chars: stripHtml(description).length,
        }),
        [description]
    );

    const baseEditorConfig = useMemo(
        () => ({
            readonly: false,
            showCharsCounter: false,
            showWordsCounter: false,
            showPoweredBy: false,
            showStatusbar: false,
            statusbar: false,
            toolbarSticky: false,
            toolbarAdaptive: false,
            extraButtons: [],
            language: 'en',
            hidePoweredByJodit: true,
            disablePlugins: ['poweredBy', 'selectall'],
            removeButtons: ['selectall', 'brushCell'],
        }),
        []
    );

    const editorConfigTitle = useMemo(
        () => ({
            ...baseEditorConfig,
            height: 200,
            buttons: ['bold', 'italic', 'underline', 'strikethrough', 'brush', 'lineHeight', 'fontsize', '|', 'undo'],
            placeholder: 'Insert Title Here...',
            askBeforePasteHTML: false,
            defaultStyle: 'text-align: center;',
            controls: {
                background: {
                    disable: true,
                },
                lineHeight: {
                    list: [0.7, 1, 1.2, 1.5],
                },
                fontsize: {
                    default: '48',
                    list: {
                        '38px': '38',
                        '40px': '40',
                        '42px': '42',
                        '48px': '48',
                        '54px': '54',
                        '58px': '58',
                        '64px': '64',
                        '72px': '72',
                        '76px': '76',
                        '80px': '80',
                        '92px': '92',
                    },
                    apply: (editor: any) => {
                        setTimeout(() => {
                            try {
                                editor.execCommand('fontSize', '48');
                            } catch (e) { }
                        }, 0);
                    },
                },
            },
            defaultActionOnPaste: 'INSERT_ONLY_TEXT',
            limitChars: 30,
            events: {
                beforeChange: (newValue: any) => {
                    const sanitized = sanitizeHtmlContent(newValue);
                    const plain = stripHtml(sanitized);
                    if (plain.length > TITLE_CHAR_LIMIT) {
                        return false;
                    }
                    return sanitized;
                },
                change: (newValue: any) => {
                    const sanitized = sanitizeHtmlContent(newValue);
                    const plain = stripHtml(sanitized);
                    if (plain.length <= TITLE_CHAR_LIMIT) {
                        titleUpdateSourceRef.current = 'editor';
                        setTitle(sanitized);
                    }
                },
                afterInit: (editor: any) => {
                    removeBackgroundStyles(editor);
                    preventBackgroundCommand(editor);
                    applyPasteLimiter(editor, TITLE_CHAR_LIMIT, titleValueRef);
                    editor?.events?.on('change', () => removeBackgroundStyles(editor));
                },
            },
        }),
        [applyPasteLimiter, baseEditorConfig]
    );

    const editorConfigDescription = useMemo(
        () => ({
            ...baseEditorConfig,
            height: 200,
            buttons: ['bold', 'italic', 'underline', 'strikethrough', 'brush', 'fontsize', 'align', '|', 'undo', 'redo'],
            placeholder: 'Insert Description Here...',
            askBeforePasteHTML: false,
            defaultStyle: 'text-align: center;',
            defaultActionOnPaste: 'INSERT_ONLY_TEXT',
            controls: {
                background: {
                    disable: true,
                },
                lineHeight: {
                    list: [1, 1.2, 1.5],
                },
            },
            events: {
                beforeChange: (newValue: any) => {
                    return sanitizeHtmlContent(newValue);
                },
                change: (newValue: any) => {
                    const sanitized = sanitizeHtmlContent(newValue);
                    descriptionUpdateSourceRef.current = 'editor';
                    setDescription(sanitized);
                },
                afterInit: (editor: any) => {
                    removeBackgroundStyles(editor);
                    preventBackgroundCommand(editor);
                    editor?.events?.on('change', () => removeBackgroundStyles(editor));
                },
            },
        }),
        [applyPasteLimiter, baseEditorConfig]
    );

    const selectedCardConfig = cardConfig.find((card: any) => card.id === selectedCard);

    const handleDownloadPdf = useCallback(async () => {
        if (isGeneratingPDF) return;
        if (!previewRef.current) {
            alert('Area preview belum siap. Silakan coba lagi.');
            return;
        }

        setIsGeneratingPDF(true);

        try {
            const [{ default: html2canvas }, jsPDFModule] = await Promise.all([import('html2canvas'), import('jspdf')]);

            const JsPDF = jsPDFModule.default || jsPDFModule.jsPDF;
            const pdf = new JsPDF({
                orientation: 'portrait',
                unit: 'mm',
                format: 'a4',
            });

            const target = previewRef.current;
            const images = target.querySelectorAll('img');

            await Promise.all(
                Array.from(images).map(
                    (img: any) =>
                        new Promise<void>((resolve) => {
                            if (img.complete && img.naturalWidth > 0) {
                                resolve();
                                return;
                            }
                            img.onload = () => resolve();
                            img.onerror = () => resolve();
                            setTimeout(() => resolve(), 3000);
                        })
                )
            );

            if (typeof document !== 'undefined' && document.fonts) {
                await document.fonts.ready;
            }

            await wait(300);

            const canvas = await html2canvas(target, {
                scale: 2,
                useCORS: true,
                allowTaint: true,
                backgroundColor: '#ffffff',
                logging: false,
            });

            const printWidth = 100;
            const printHeight = 155;
            const pdfWidth = pdf.internal.pageSize.getWidth();
            const centerX = (pdfWidth - printWidth) / 2;
            const centerY = 10;

            const imgData = canvas.toDataURL('image/png', 1.0);
            const canvasAspectRatio = canvas.width / canvas.height;
            const printAspectRatio = printWidth / printHeight;

            let imgWidth = printWidth;
            let imgHeight = printHeight;

            if (canvasAspectRatio > printAspectRatio) {
                imgHeight = imgWidth / canvasAspectRatio;
            } else {
                imgWidth = imgHeight * canvasAspectRatio;
            }

            const xOffset = centerX + (printWidth - imgWidth) / 2;

            const borderSize = 1; // 3mm
            pdf.setDrawColor(0, 0, 0); // Hitam
            pdf.setLineWidth(0.1);
            pdf.rect(xOffset - borderSize, centerY - borderSize, imgWidth + borderSize * 2, imgHeight + borderSize * 2);

            pdf.addImage(imgData, 'PNG', xOffset, centerY, imgWidth, imgHeight);
            pdf.save('gift-card-preview.pdf');
        } catch (error) {
            console.error('Gagal membuat PDF', error);
            alert('Terjadi kesalahan saat membuat PDF. Silakan coba lagi.');
        } finally {
            setIsGeneratingPDF(false);
        }
    }, [isGeneratingPDF]);

    return (
        <>
            <Head>
                <title>Generate Gift Card | Aurum Lab</title>
                <meta name="description" content="Buat gift card custom dengan desain pilihan Anda" />
            </Head>
            <main className="min-h-[85vh] w-full px-4 py-6">
                <div className="mx-auto flex w-full max-w-6xl flex-col gap-8 lg:flex-row">
                    <div className="w-full lg:max-w-xl">
                        <div className="my-4">
                            <h3 className="my-1 text-xl">Pilih Desain Card</h3>
                            <select
                                value={selectedCard}
                                onChange={(e) => setSelectedCard(e.target.value)}
                                className="w-full rounded-lg border border-gray-300 px-4 py-2 focus:border-transparent focus:outline-none focus:ring-2 bg-white dark:bg-gray-800"
                            >
                                {cardConfig.map((card: any) => (
                                    <option key={card.id} value={card.id} className="text-gray-600">
                                        {card.id}
                                    </option>
                                ))}
                            </select>
                        </div>

                        <div className="my-4">
                            <div className="mt-1 text-sm text-gray-600">
                                Title ({titleStats.chars}/{TITLE_CHAR_LIMIT})
                            </div>
                            <JoditEditor key={`title-editor-${selectedCard}`} value={title} config={editorConfigTitle as any} />
                        </div>

                        <div className="my-4">
                            <div className="mt-1 text-sm text-gray-600">
                                Description ({descriptionStats.chars} karakter)
                            </div>
                            <JoditEditor key={`description-editor-${selectedCard}`} value={description} config={editorConfigDescription as any} />
                        </div>
                    </div>

                    <div className="w-full lg:flex-1">
                        <div className="sticky top-6 space-y-4">
                            <h3 className="mb-1 text-lg font-semibold">Preview</h3>
                            <GiftCardDisplay ref={previewRef} previewHeader={title} previewContent={description} ImageVariation={selectedCardConfig?.image} ImageValue={selectedCard} />
                            <button
                                onClick={handleDownloadPdf}
                                disabled={isGeneratingPDF}
                                className="w-full rounded-lg bg-yellow-600 px-4 py-3 text-white transition-colors hover:bg-yellow-700 focus:outline-none focus:ring-2 focus:ring-yellow-600 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-60"
                            >
                                {isGeneratingPDF ? 'Membuat PDF...' : 'Download PDF'}
                            </button>
                        </div>
                    </div>
                </div>
            </main>
        </>
    );
};

export default GenerateGiftCard;
