import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
import { useFormContext } from 'react-hook-form';
import { RichTextEditor } from 'react-summernote-light';

// Store files to be uploaded later with their blob URLs
const pendingUploads: Map<string, File> = new Map();

function debounce<T extends (...args: any[]) => any>(func: T, wait: number) {
    let timeout: NodeJS.Timeout | undefined;
    return function (this: any, ...args: Parameters<T>) {
        const context = this;
        clearTimeout(timeout);
        timeout = setTimeout(() => func.apply(context, args), wait);
    };
}

// Function to upload all pending files
export async function uploadPendingFiles(): Promise<Map<string, string>> {
    const uploadPromises: Promise<{ blobUrl: string; finalUrl: string }>[] = [];

    for (const [blobUrl, file] of pendingUploads.entries()) {
        const uploadPromise = new Promise<{ blobUrl: string; finalUrl: string }>((resolve, reject) => {
            const isImage = file.type.startsWith('image/');
            const folder = isImage ? 'images/blog' : 'videos/blog';
            const fileName = `${folder}/${Date.now()}-${file.name}`;
            // Replace this with your actual file upload implementation
            // For now, we'll just resolve with a mock URL
            resolve({
                blobUrl,
                finalUrl: blobUrl, // In a real implementation, this would be the uploaded file URL
            });
        });

        uploadPromises.push(uploadPromise);
    }

    try {
        const results = await Promise.all(uploadPromises);
        const urlMapping = new Map<string, string>();

        results.forEach(({ blobUrl, finalUrl }) => {
            urlMapping.set(blobUrl, finalUrl);
        });

        // Clear pending uploads after successful upload
        pendingUploads.clear();

        return urlMapping;
    } catch (error) {
        console.error('Error uploading files:', error);
        throw error;
    }
}

// Function to replace blob URLs with final URLs in content
export function replaceTemporaryUrls(content: string, urlMapping: Map<string, string>): string {
    let updatedContent = content;

    for (const [blobUrl, finalUrl] of urlMapping.entries()) {
        // Replace in img src attributes
        updatedContent = updatedContent.replace(new RegExp(`src="${escapeRegExp(blobUrl)}"`, 'g'), `src="${finalUrl}"`);

        // Replace in iframe src attributes (for videos)
        updatedContent = updatedContent.replace(new RegExp(`src="${escapeRegExp(blobUrl)}"`, 'g'), `src="${finalUrl}"`);

        // Replace any other occurrences
        updatedContent = updatedContent.replace(new RegExp(escapeRegExp(blobUrl), 'g'), finalUrl);
    }

    return updatedContent;
}

function escapeRegExp(string: string) {
    return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

// Get all blob URLs from content
export function extractBlobUrls(content: string): string[] {
    const blobUrls: string[] = [];
    const blobRegex = /blob:https?:\/\/[^\s"'>]+/g;
    let match;

    while ((match = blobRegex.exec(content)) !== null) {
        blobUrls.push(match[0]);
    }

    return blobUrls;
}

export interface TextEditorRef {
    insertTextAtCursor: (text: string) => void;
}

interface TextEditorProps {
    name?: string;
    label?: string;
    minHeight?: string;
    className?: string;
}

const TextEditor = forwardRef<TextEditorRef, TextEditorProps>(({ name = 'description', label, minHeight = '400px', className }, ref) => {
    const id = `text-editor-${name}`;
    const formCtx = useFormContext() as any | null;
    const setValueRef = useRef<(name: string, value: any, opts?: any) => void>(formCtx?.setValue ?? (() => {}));
    const watchRef = useRef<(name?: string) => any>(formCtx?.watch ?? (() => undefined));
    // Update refs whenever form context changes
    useEffect(() => {
        setValueRef.current = formCtx?.setValue ?? (() => {});
        watchRef.current = formCtx?.watch ?? (() => undefined);
    }, [formCtx]);

    // Warn once if the editor is used outside a react-hook-form FormProvider
    const warnedMissingFormRef = useRef(false);
    useEffect(() => {
        if (!formCtx && !warnedMissingFormRef.current) {
            // eslint-disable-next-line no-console
            console.warn(
                '[TextEditor] react-hook-form context is missing. Editor will work, but changes will not be written to a form. Wrap with <FormProvider> or pass setValue/watch via props.',
            );
            warnedMissingFormRef.current = true;
        }
    }, [formCtx]);

    const value = watchRef.current(name);
    const [content, setContent] = useState(value || '');

    // Expose methods to parent component
    useImperativeHandle(ref, () => ({
        insertTextAtCursor: (text: string) => {
            try {
                // For RichTextEditor, we'll append the text to current content
                const currentContent = content || '';
                const newContent = currentContent + text;
                setContent(newContent);
                setValueRef.current(name, newContent, {
                    shouldValidate: true,
                    shouldDirty: true,
                });
            } catch (error) {
                console.warn('Error inserting text at cursor:', error);
            }
        },
    }));

    // Sync local state with form value
    useEffect(() => {
        setContent(value || '');
    }, [value]);

    const handleEditorChange = useCallback(
        debounce((content: string) => {
            // Use the stable ref for setValue (no-op outside form)
            setValueRef.current(name, content, {
                shouldValidate: true,
                shouldDirty: true,
            });
            setContent(content);
        }, 300),
        [name],
    );

    return (
        <div className="w-full">
            {label ? (
                <label htmlFor={id} className="mb-2 block text-sm font-medium text-gray-700">
                    {label}
                </label>
            ) : null}
            <RichTextEditor
                id={id}
                initialValue={content}
                onChange={handleEditorChange}
                placeholder="Write here..."
                minHeight={minHeight}
                enableCodeView
                enableFullscreen
                className={className ?? 'w-full'}
            />
        </div>
    );
});

TextEditor.displayName = 'TextEditor';

export default TextEditor;
