'use client'
import {useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react';
import {EditorContent} from '@tiptap/react';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {
    faAlignCenter,
    faAlignLeft,
    faAlignRight,
    faBold,
    faCog,
    faFloppyDisk,
    faGhost,
    faHeading,
    faLayerGroup,
    faListOl,
    faListUl,
    faParagraph,
    faUnderline
} from '@fortawesome/free-solid-svg-icons';
import {EditorContext} from "@/context/EditorContext";
import {ChapterContext} from '@/context/ChapterContext';
import System from '@/lib/models/System';
import {AlertContext} from '@/context/AlertContext';
import {SessionContext} from "@/context/SessionContext";
import {BookContext} from '@/context/BookContext';
import DraftCompanion from "@/components/editor/DraftCompanion";
import GhostWriter from "@/components/ghostwriter/GhostWriter";
import SubmitButtonWLoading from "@/components/form/SubmitButtonWLoading";
import CollapsableButton from "@/components/CollapsableButton";
import {IconDefinition} from "@fortawesome/fontawesome-svg-core";
import UserEditorSettings, {EditorDisplaySettings} from "@/components/editor/UserEditorSetting";
import {useTranslations} from "next-intl";
import {LangContext, LangContextProps} from "@/context/LangContext";
import OfflineContext, {OfflineContextType} from "@/context/OfflineContext";

interface ToolbarButton {
    action: () => void;
    icon: IconDefinition;
    isActive: boolean;
    label?: string;
}

interface EditorClasses {
    base: string;
    h1: string;
    h2: string;
    h3: string;
    container: string;
    theme: string;
    paragraph: string;
    lists: string;
    listItems: string;
}

const DEFAULT_EDITOR_SETTINGS: EditorDisplaySettings = {
    zoomLevel: 3,
    indent: 30,
    lineHeight: 1.5,
    theme: 'sombre',
    fontFamily: 'lora',
    maxWidth: 768,
    focusMode: false
};

const FONT_SIZE_CLASSES = {
    1: 'text-sm',
    2: 'text-base',
    3: 'text-lg',
    4: 'text-xl',
    5: 'text-2xl'
} as const;

const H1_SIZE_CLASSES = {
    1: 'text-xl',
    2: 'text-2xl',
    3: 'text-3xl',
    4: 'text-4xl',
    5: 'text-5xl'
} as const;

const H2_SIZE_CLASSES = {
    1: 'text-lg',
    2: 'text-xl',
    3: 'text-2xl',
    4: 'text-3xl',
    5: 'text-4xl'
} as const;

const H3_SIZE_CLASSES = {
    1: 'text-base',
    2: 'text-lg',
    3: 'text-xl',
    4: 'text-2xl',
    5: 'text-3xl'
} as const;

const FONT_FAMILY_CLASSES = {
    'lora': 'Lora',
    'serif': 'font-serif',
    'sans-serif': 'font-sans',
    'monospace': 'font-mono'
} as const;

const LINE_HEIGHT_CLASSES = {
    1.2: 'leading-tight',
    1.5: 'leading-normal',
    1.75: 'leading-relaxed',
    2: 'leading-loose'
} as const;

const MAX_WIDTH_CLASSES = {
    600: 'max-w-xl',
    650: 'max-w-2xl',
    700: 'max-w-3xl',
    750: 'max-w-4xl',
    800: 'max-w-5xl',
    850: 'max-w-6xl',
    900: 'max-w-7xl',
    950: 'max-w-full',
    1000: 'max-w-full',
    1050: 'max-w-full',
    1100: 'max-w-full',
    1150: 'max-w-full',
    1200: 'max-w-full'
} as const;

function getClosestKey<T extends Record<number, any>>(value: number, obj: T): keyof T {
    const keys: number[] = Object.keys(obj).map(Number).sort((a: number, b: number): number => a - b);
    return keys.reduce((prev: number, curr: number): number =>
        Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev
    );
}

export default function TextEditor() {
    const t = useTranslations();
    const {lang} = useContext<LangContextProps>(LangContext)
    const {editor} = useContext(EditorContext);
    const {chapter} = useContext(ChapterContext);
    const {book} = useContext(BookContext);
    const {errorMessage, successMessage} = useContext(AlertContext);
    const {session} = useContext(SessionContext);
    const {isCurrentlyOffline} = useContext<OfflineContextType>(OfflineContext);
    
    const [mainTimer, setMainTimer] = useState<number>(0);
    const [showDraftCompanion, setShowDraftCompanion] = useState<boolean>(false);
    const [showGhostWriter, setShowGhostWriter] = useState<boolean>(false);
    const [showUserSettings, setShowUserSettings] = useState<boolean>(false);
    const [isSaving, setIsSaving] = useState<boolean>(false);
    const [editorSettings, setEditorSettings] = useState<EditorDisplaySettings>(DEFAULT_EDITOR_SETTINGS);
    const [editorClasses, setEditorClasses] = useState<EditorClasses>({
        base: 'text-lg font-serif leading-normal',
        h1: 'text-3xl font-bold',
        h2: 'text-2xl font-bold',
        h3: 'text-xl font-bold',
        container: 'max-w-3xl',
        theme: 'bg-tertiary/90 backdrop-blur-sm bg-opacity-25 text-text-primary',
        paragraph: 'indent-6',
        lists: 'pl-10',
        listItems: 'text-lg'
    });
    
    const timerRef: React.RefObject<number | null> = useRef<number | null>(null);
    const timeoutRef: React.RefObject<number | null> = useRef<number | null>(null);
    
    const updateEditorClasses: (settings: EditorDisplaySettings) => void = useCallback((settings: EditorDisplaySettings): void => {
        const fontSizeKey = settings.zoomLevel as keyof typeof FONT_SIZE_CLASSES;
        const h1SizeKey = settings.zoomLevel as keyof typeof H1_SIZE_CLASSES;
        const h2SizeKey = settings.zoomLevel as keyof typeof H2_SIZE_CLASSES;
        const h3SizeKey = settings.zoomLevel as keyof typeof H3_SIZE_CLASSES;
        const fontFamilyKey = settings.fontFamily as keyof typeof FONT_FAMILY_CLASSES;
        const lineHeightKey = settings.lineHeight as keyof typeof LINE_HEIGHT_CLASSES;
        const maxWidthKey: number = getClosestKey(settings.maxWidth, MAX_WIDTH_CLASSES);
        
        const indentClass = `indent-${Math.round(settings.indent / 4)}`;
        
        const baseClass = `${FONT_SIZE_CLASSES[fontSizeKey]} ${FONT_FAMILY_CLASSES[fontFamilyKey]} ${LINE_HEIGHT_CLASSES[lineHeightKey]}`;
        const h1Class = `${H1_SIZE_CLASSES[h1SizeKey]} font-bold ${FONT_FAMILY_CLASSES[fontFamilyKey]} ${LINE_HEIGHT_CLASSES[lineHeightKey]}`;
        const h2Class = `${H2_SIZE_CLASSES[h2SizeKey]} font-bold ${FONT_FAMILY_CLASSES[fontFamilyKey]} ${LINE_HEIGHT_CLASSES[lineHeightKey]}`;
        const h3Class = `${H3_SIZE_CLASSES[h3SizeKey]} font-bold ${FONT_FAMILY_CLASSES[fontFamilyKey]} ${LINE_HEIGHT_CLASSES[lineHeightKey]}`;
        const containerClass = MAX_WIDTH_CLASSES[maxWidthKey as keyof typeof MAX_WIDTH_CLASSES];
        const listsClass = `pl-${Math.round((settings.indent + 20) / 4)}`;
        
        let themeClass: string = '';
        switch (settings.theme) {
            case 'clair':
                themeClass = 'bg-white text-black';
                break;
            case 'sépia':
                themeClass = 'text-amber-900';
                break;
            default:
                themeClass = 'bg-tertiary/90 backdrop-blur-sm bg-opacity-25 text-text-primary';
        }
        
        setEditorClasses({
            base: baseClass,
            h1: h1Class,
            h2: h2Class,
            h3: h3Class,
            container: containerClass,
            theme: themeClass,
            paragraph: indentClass,
            lists: listsClass,
            listItems: baseClass
        });
    }, []);
    
    const containerStyle = useMemo(() => {
        if (editorSettings.theme === 'sépia') {
            return {backgroundColor: '#f4f1e8'};
        }
        return {};
    }, [editorSettings.theme]);
    
    const toolbarButtons: ToolbarButton[] = (() => {
        if (!editor) return [];

        return [
            {
                action: (): boolean => editor.chain().focus().setParagraph().run(),
                icon: faParagraph,
                isActive: editor.isActive('paragraph')
            },
            {
                action: (): boolean => editor.chain().focus().toggleBold().run(),
                icon: faBold,
                isActive: editor.isActive('bold')
            },
            {
                action: (): boolean => editor.chain().focus().toggleUnderline().run(),
                icon: faUnderline,
                isActive: editor.isActive('underline')
            },
            {
                action: (): boolean => editor.chain().focus().setTextAlign('left').run(),
                icon: faAlignLeft,
                isActive: editor.isActive({textAlign: 'left'})
            },
            {
                action: (): boolean => editor.chain().focus().setTextAlign('center').run(),
                icon: faAlignCenter,
                isActive: editor.isActive({textAlign: 'center'})
            },
            {
                action: (): boolean => editor.chain().focus().setTextAlign('right').run(),
                icon: faAlignRight,
                isActive: editor.isActive({textAlign: 'right'})
            },
            {
                action: (): boolean => editor.chain().focus().toggleBulletList().run(),
                icon: faListUl,
                isActive: editor.isActive('bulletList')
            },
            {
                action: (): boolean => editor.chain().focus().toggleOrderedList().run(),
                icon: faListOl,
                isActive: editor.isActive('orderedList')
            },
            {
                action: (): boolean => editor.chain().focus().toggleHeading({level: 1}).run(),
                icon: faHeading,
                isActive: editor.isActive('heading', {level: 1}),
                label: '1'
            },
            {
                action: (): boolean => editor.chain().focus().toggleHeading({level: 2}).run(),
                icon: faHeading,
                isActive: editor.isActive('heading', {level: 2}),
                label: '2'
            },
            {
                action: (): boolean => editor.chain().focus().toggleHeading({level: 3}).run(),
                icon: faHeading,
                isActive: editor.isActive('heading', {level: 3}),
                label: '3'
            },
        ];
    })();
    
    const saveContent: () => Promise<void> = useCallback(async (): Promise<void> => {
        if (!editor || !chapter) return;
        
        setIsSaving(true);
        const content = editor.state.doc.toJSON();
        const chapterId: string = chapter.chapterId || '';
        const version: number = chapter.chapterContent.version || 0;
        
        try {
            let response: boolean;
            if (isCurrentlyOffline()){
                response = await window.electron.invoke<boolean>('db:chapter:content:save',{
                    chapterId,
                    version,
                    content,
                    totalWordCount: editor.getText().length,
                    currentTime: mainTimer
                })
            } else {
                if (book?.localBook) {
                    response = await window.electron.invoke<boolean>('db:chapter:content:save',{
                        chapterId,
                        version,
                        content,
                        totalWordCount: editor.getText().length,
                        currentTime: mainTimer
                    })
                } else {
                    response = await System.authPostToServer<boolean>(`chapter/content`, {
                        chapterId,
                        version,
                        content,
                        totalWordCount: editor.getText().length,
                        currentTime: mainTimer
                    }, session?.accessToken, lang);
                }
            }
            if (!response) {
                errorMessage(t('editor.error.savedFailed'));
                setIsSaving(false);
                return;
            }
            setMainTimer(0);
            successMessage(t('editor.success.saved'));
            setIsSaving(false);
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t('editor.error.unknownError'));
            }
            setIsSaving(false);
        }
    }, [editor, chapter, mainTimer, session?.accessToken, successMessage, errorMessage]);
    
    const handleShowDraftCompanion: () => void = useCallback((): void => {
        setShowDraftCompanion((prev: boolean): boolean => !prev);
        setShowGhostWriter(false);
        setShowUserSettings(false);
    }, []);
    
    const handleShowGhostWriter: () => void = useCallback((): void => {
        if (chapter?.chapterContent.version === 2) {
            setShowGhostWriter((prev: boolean): boolean => !prev);
            setShowDraftCompanion(false);
            setShowUserSettings(false);
        }
    }, [chapter?.chapterContent.version]);
    
    const handleShowUserSettings: () => void = useCallback((): void => {
        setShowUserSettings((prev: boolean): boolean => !prev);
        setShowDraftCompanion(false);
        setShowGhostWriter(false);
    }, []);
    
    useEffect((): void => {
        if (!editor) return;
        
        const editorElement: HTMLElement = editor.view.dom;
        if (editorElement) {
            const indentClasses: string[] = Array.from({length: 21}, (_, i) => `indent-${i}`);
            editorElement.classList.remove(...indentClasses);
            
            if (editorClasses.paragraph) {
                editorElement.classList.add(editorClasses.paragraph);
            }
        }
    }, [editor, editorClasses.paragraph]);
    
    useEffect((): void => {
        updateEditorClasses(editorSettings);
    }, [editorSettings, updateEditorClasses]);
    
    useEffect((): () => void => {
        function startTimer(): void {
            if (timerRef.current === null) {
                timerRef.current = window.setInterval(() => {
                    setMainTimer(prevTimer => prevTimer + 1);
                }, 1000);
            }
        }
        
        function stopTimer(): void {
            if (timerRef.current !== null) {
                clearInterval(timerRef.current);
                timerRef.current = null;
            }
        }
        
        function resetTimeout(): void {
            if (timeoutRef.current !== null) {
                clearTimeout(timeoutRef.current);
            }
            timeoutRef.current = window.setTimeout(stopTimer, 5000);
        }
        
        function handleKeyDown(): void {
            startTimer();
            resetTimeout();
        }
        
        window.addEventListener('keydown', handleKeyDown, {passive: true});
        
        return (): void => {
            window.removeEventListener('keydown', handleKeyDown);
            if (timerRef.current !== null) {
                clearInterval(timerRef.current);
            }
            if (timeoutRef.current !== null) {
                clearTimeout(timeoutRef.current);
            }
        };
    }, []);
    
    useEffect((): () => void => {
        document.addEventListener('keydown', handleKeyDown, {passive: false});
        return (): void => document.removeEventListener('keydown', handleKeyDown);
    }, [saveContent]);
    
    useEffect((): void => {
        if (!editor) return;
        if (chapter?.chapterContent.content) {
            try {
                const parsedContent = JSON.parse(chapter.chapterContent.content);
                editor.commands.setContent(parsedContent);
            } catch (error) {
                editor.commands.setContent({
                    type: "doc",
                    content: [{type: "paragraph", content: []}]
                });
            }
        } else {
            editor.commands.setContent({
                type: "doc",
                content: [{type: "paragraph", content: []}]
            });
        }
        
        if (chapter?.chapterContent.version !== 2) {
            setShowGhostWriter(false);
        }
    }, [editor, chapter?.chapterContent.content, chapter?.chapterContent.version]);
    
    async function handleKeyDown(event: KeyboardEvent): Promise<void> {
        if ((event.ctrlKey || event.metaKey) && event.key === 's') {
            event.preventDefault();
            await saveContent();
        }
    }
    
    if (!editor) {
        return null;
    }
    
    return (
        <div className="flex flex-col flex-1 w-full h-full">
            <div
                className={`flex justify-between gap-3 border-b border-secondary/30 px-4 py-3 bg-gradient-to-b from-dark-background/80 to-dark-background/50 backdrop-blur-sm transition-opacity duration-300 shadow-md ${editorSettings.focusMode ? 'opacity-70 hover:opacity-100' : ''}`}>
                <div className="flex flex-wrap gap-1">
                    {toolbarButtons.map((button: ToolbarButton, index: number) => (
                        <button
                            key={index}
                            onClick={button.action}
                            className={`group flex items-center px-3 py-2 rounded-lg transition-all duration-200 ${button.isActive ? 'bg-primary text-text-primary shadow-md shadow-primary/30 scale-105' : 'text-muted hover:text-text-primary hover:bg-secondary/50 hover:shadow-sm hover:scale-105'}`}
                        >
                            <FontAwesomeIcon icon={button.icon} className={'w-4 h-4 transition-transform duration-200 group-hover:scale-110'}/>
                            {
                                button.label &&
                                <span className="ml-2 text-sm font-medium">
                                {t(`textEditor.toolbar.${button.label}`)}
                            </span>
                            }
                        </button>
                    ))}
                </div>
                <div className="flex items-center gap-2">
                    <CollapsableButton
                        showCollapsable={showUserSettings}
                        text={t("textEditor.preferences")}
                        onClick={handleShowUserSettings}
                        icon={faCog}
                    />
                    {chapter?.chapterContent.version === 2 && !isCurrentlyOffline() && !book?.localBook && (
                        <CollapsableButton
                            showCollapsable={showGhostWriter}
                            text={t("textEditor.ghostWriter")}
                            onClick={handleShowGhostWriter}
                            icon={faGhost}
                        />
                    )}
                    {chapter?.chapterContent.version && chapter.chapterContent.version > 2 && (
                        <CollapsableButton
                            showCollapsable={showDraftCompanion}
                            text={t("textEditor.draftCompanion")}
                            onClick={handleShowDraftCompanion}
                            icon={faLayerGroup}
                        />
                    )}
                    <SubmitButtonWLoading
                        callBackAction={saveContent}
                        isLoading={isSaving}
                        text={t("textEditor.save")}
                        loadingText={t("textEditor.saving")}
                        icon={faFloppyDisk}
                    />
                </div>
            </div>

            <div className="flex justify-between w-full h-full overflow-auto">
                <div
                    className={`flex-1 p-8 overflow-auto transition-all duration-300 ${editorSettings.focusMode ? 'bg-black/20' : ''}`}>
                    <div
                        className={`editor-container mx-auto p-6 rounded-2xl shadow-2xl min-h-[80%] border border-secondary/50 ${editorClasses.container} ${editorClasses.theme} relative`}
                        style={containerStyle}>
                        <div
                            className="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-transparent via-primary/30 to-transparent"></div>
                        <div
                            className="absolute bottom-0 left-0 w-full h-1 bg-gradient-to-r from-transparent via-primary/30 to-transparent"></div>
                        <EditorContent className={`w-full h-full ${editorClasses.base} editor-content`}
                                       editor={editor}/>
                    </div>
                </div>
                
                {(showDraftCompanion || showGhostWriter || showUserSettings) && (
                    <div
                        className={`w-4/12 transition-opacity duration-300 ${editorSettings.focusMode ? 'opacity-50 hover:opacity-100' : ''}`}>
                        {showDraftCompanion && <DraftCompanion/>}
                        {showGhostWriter && <GhostWriter/>}
                        {showUserSettings && (
                            <UserEditorSettings
                                settings={editorSettings}
                                onSettingsChange={setEditorSettings}
                            />
                        )}
                    </div>
                )}
            </div>
        </div>
    );
}