'use client'
import React, {useCallback, useContext, useEffect, useRef, useState} from 'react';
import {EditorContent, JSONContent} from '@tiptap/react';
import {
    AlignCenter,
    AlignJustify,
    AlignLeft,
    AlignRight,
    Bold,
    Ghost,
    Heading1,
    Heading2,
    Heading3,
    Layers,
    List,
    ListOrdered,
    LucideIcon,
    Save,
    SlidersHorizontal,
    Underline
} from 'lucide-react';
import {EditorContext, EditorContextProps} from "@/context/EditorContext";
import {ChapterContext, ChapterContextProps} from '@/context/ChapterContext';
import {BookContext, BookContextProps} from "@/context/BookContext";
import {apiPost} from '@/lib/api/client';
import {isDesktop} from '@/lib/configs';
import * as tauri from '@/lib/tauri';
import OfflineContext, {OfflineContextType} from '@/context/OfflineContext';
import {AlertContext, AlertContextProps} from '@/context/AlertContext';
import {SessionContext, SessionContextProps} from "@/context/SessionContext";
import DraftCompanion from "@/components/editor/DraftCompanion";
import GhostWriter from "@/components/ghostwriter/GhostWriter";
import IconButton from "@/components/ui/IconButton";
import UserEditorSettings, {EditorDisplaySettings} from "@/components/editor/UserEditorSetting";
import {useTranslations} from '@/lib/i18n';
import {LangContext, LangContextProps} from "@/context/LangContext";

interface ToolbarButton {
    action: () => void;
    icon: LucideIcon;
    isActive: boolean;
}

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

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

const fontSizeClasses: Record<number, string> = {
    1: 'text-sm',
    2: 'text-base',
    3: 'text-lg',
    4: 'text-xl',
    5: 'text-2xl'
};

const h1SizeClasses: Record<number, string> = {
    1: 'text-xl',
    2: 'text-2xl',
    3: 'text-3xl',
    4: 'text-4xl',
    5: 'text-5xl'
};

const h2SizeClasses: Record<number, string> = {
    1: 'text-lg',
    2: 'text-xl',
    3: 'text-2xl',
    4: 'text-3xl',
    5: 'text-4xl'
};

const h3SizeClasses: Record<number, string> = {
    1: 'text-base',
    2: 'text-lg',
    3: 'text-xl',
    4: 'text-2xl',
    5: 'text-3xl'
};

const fontFamilyClasses: Record<string, string> = {
    'lora': 'Lora',
    'serif': 'font-serif',
    'sans-serif': 'font-sans',
    'monospace': 'font-mono'
};

const lineHeightClasses: Record<number, string> = {
    1.2: 'leading-tight',
    1.5: 'leading-normal',
    1.75: 'leading-relaxed',
    2: 'leading-loose'
};

const maxWidthClasses: Record<number, string> = {
    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'
};

function getClosestKey(value: number, obj: Record<number, string>): number {
    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}: LangContextProps = useContext<LangContextProps>(LangContext)
    const {editor}: EditorContextProps = useContext<EditorContextProps>(EditorContext);
    const {chapter}: ChapterContextProps = useContext<ChapterContextProps>(ChapterContext);
    const {book}: BookContextProps = useContext<BookContextProps>(BookContext);
    const {errorMessage, successMessage}: AlertContextProps = useContext<AlertContextProps>(AlertContext);
    const {session}: SessionContextProps = useContext<SessionContextProps>(SessionContext);
    const {isCurrentlyOffline}: OfflineContextType = 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>(defaultEditorSettings);
    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 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 zoomKey: number = getClosestKey(settings.zoomLevel, fontSizeClasses);
        const lineHeightKey: number = getClosestKey(settings.lineHeight, lineHeightClasses);
        const maxWidthKey: number = getClosestKey(settings.maxWidth, maxWidthClasses);
        
        const fontFamily: string = fontFamilyClasses[settings.fontFamily] || fontFamilyClasses['lora'];
        const lineHeight: string = lineHeightClasses[lineHeightKey];
        const indentClass: string = `indent-${Math.round(settings.indent / 4)}`;
        
        const baseClass: string = `${fontSizeClasses[zoomKey]} ${fontFamily} ${lineHeight}`;
        const h1Class: string = `${h1SizeClasses[zoomKey]} font-bold ${fontFamily} ${lineHeight}`;
        const h2Class: string = `${h2SizeClasses[zoomKey]} font-bold ${fontFamily} ${lineHeight}`;
        const h3Class: string = `${h3SizeClasses[zoomKey]} font-bold ${fontFamily} ${lineHeight}`;
        const containerClass: string = maxWidthClasses[maxWidthKey];
        const listsClass: string = `pl-${Math.round((settings.indent + 20) / 4)}`;
        
        let themeClass: string = '';
        switch (settings.theme) {
            case 'clair':
                themeClass = 'bg-gray-light text-darkest-background';
                break;
            case 'sépia':
                themeClass = 'bg-editor-page-sepia text-darkest-background';
                break;
            default:
                themeClass = 'bg-tertiary text-text-primary';
        }
        
        setEditorClasses({
            base: baseClass,
            h1: h1Class,
            h2: h2Class,
            h3: h3Class,
            container: containerClass,
            theme: themeClass,
            paragraph: indentClass,
            lists: listsClass,
            listItems: baseClass
        });
    }, []);
    
    const editorContainerRef: React.RefObject<HTMLDivElement | null> = useRef<HTMLDivElement>(null);
    
    const editorContainerBgClass: string =
        editorSettings.theme === 'sépia' ? 'bg-editor-page-sepia' : '';
    
    const toolbarButtons: ToolbarButton[] = ((): ToolbarButton[] => {
        if (!editor) return [];

        return [
            {
                action: (): boolean => editor.chain().focus().setParagraph().run(),
                icon: AlignJustify,
                isActive: editor.isActive('paragraph')
            },
            {
                action: (): boolean => editor.chain().focus().toggleBold().run(),
                icon: Bold,
                isActive: editor.isActive('bold')
            },
            {
                action: (): boolean => editor.chain().focus().toggleUnderline().run(),
                icon: Underline,
                isActive: editor.isActive('underline')
            },
            {
                action: (): boolean => editor.chain().focus().setTextAlign('left').run(),
                icon: AlignLeft,
                isActive: editor.isActive({textAlign: 'left'})
            },
            {
                action: (): boolean => editor.chain().focus().setTextAlign('center').run(),
                icon: AlignCenter,
                isActive: editor.isActive({textAlign: 'center'})
            },
            {
                action: (): boolean => editor.chain().focus().setTextAlign('right').run(),
                icon: AlignRight,
                isActive: editor.isActive({textAlign: 'right'})
            },
            {
                action: (): boolean => editor.chain().focus().toggleBulletList().run(),
                icon: List,
                isActive: editor.isActive('bulletList')
            },
            {
                action: (): boolean => editor.chain().focus().toggleOrderedList().run(),
                icon: ListOrdered,
                isActive: editor.isActive('orderedList')
            },
            {
                action: (): boolean => editor.chain().focus().toggleHeading({level: 1}).run(),
                icon: Heading1,
                isActive: editor.isActive('heading', {level: 1})
            },
            {
                action: (): boolean => editor.chain().focus().toggleHeading({level: 2}).run(),
                icon: Heading2,
                isActive: editor.isActive('heading', {level: 2})
            },
            {
                action: (): boolean => editor.chain().focus().toggleHeading({level: 3}).run(),
                icon: Heading3,
                isActive: editor.isActive('heading', {level: 3})
            },
        ];
    })();
    
    const saveContent: () => Promise<void> = useCallback(async (): Promise<void> => {
        if (!editor || !chapter) return;
        
        setIsSaving(true);
        const content: Record<string, unknown> = editor.state.doc.toJSON();
        const chapterId: string = chapter.chapterId || '';
        const version: number = chapter.chapterContent.version || 0;
        
        try {
            let response: boolean;
            if (isDesktop && (isCurrentlyOffline() || book?.localBook)) {
                response = await tauri.saveChapterContent({
                    chapterId,
                    version,
                    content,
                    totalWordCount: editor.getText().length,
                    contentId: '',
                });
            } else {
                response = await apiPost<boolean>(`chapter/content`, {
                    chapterId,
                    version,
                    content,
                    totalWordCount: editor.getText().length,
                    currentTime: mainTimer
                }, session?.accessToken ?? '');
            }
            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}, (_: unknown, i: number): string => `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((): void => {
                    setMainTimer((prevTimer: number): number => 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: JSONContent = JSON.parse(chapter.chapterContent.content);
                editor.commands.setContent(parsedContent);
            } catch (e: unknown) {
                errorMessage(t('editor.error.parsingContent'));
                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 bg-tertiary">
            <div
                className={`flex justify-between items-center gap-3 rounded-xl mx-1 mb-1 px-4 py-2 bg-darkest-background transition-opacity duration-300 ${editorSettings.focusMode ? 'opacity-70 hover:opacity-100' : ''}`}>
                <div className="flex flex-wrap gap-1">
                    {toolbarButtons.map((button: ToolbarButton, index: number) => (
                        <IconButton
                            key={index}
                            icon={button.icon}
                            variant="ghost"
                            shape="square"
                            selected={button.isActive}
                            onClick={button.action}
                        />
                    ))}
                </div>
                <div className="flex items-center gap-1">
                    <IconButton
                        icon={SlidersHorizontal}
                        variant="ghost"
                        shape="square"
                        selected={showUserSettings}
                        onClick={handleShowUserSettings}
                        tooltip={t("textEditor.preferences")}
                    />
                    {chapter?.chapterContent.version === 2 && book?.quillsenseEnabled !== false && (
                        <IconButton
                            icon={Ghost}
                            variant="ghost"
                            shape="square"
                            selected={showGhostWriter}
                            onClick={handleShowGhostWriter}
                            tooltip={t("textEditor.ghostWriter")}
                        />
                    )}
                    {chapter?.chapterContent.version && chapter.chapterContent.version > 2 && (
                        <IconButton
                            icon={Layers}
                            variant="ghost"
                            shape="square"
                            selected={showDraftCompanion}
                            onClick={handleShowDraftCompanion}
                            tooltip={t("textEditor.draftCompanion")}
                        />
                    )}
                    <IconButton
                        icon={Save}
                        variant="ghost"
                        shape="square"
                        onClick={saveContent}
                        tooltip={t("textEditor.save")}
                    />
                </div>
            </div>
            
            <div className="flex flex-1 min-h-0 bg-tertiary">
                <div
                    className={`flex-1 bg-background rounded-xl mx-1 overflow-hidden transition-all duration-300 ${editorSettings.focusMode ? 'bg-darkest-background' : ''}`}>
                    <div className="h-full overflow-auto p-4">
                        <div
                            ref={editorContainerRef}
                            className={`editor-container mx-auto p-4 rounded-xl min-h-full ${editorClasses.container} ${editorClasses.theme} ${editorContainerBgClass}`}>
                            <EditorContent className={`w-full h-full ${editorClasses.base} editor-content`}
                                           editor={editor}/>
                        </div>
                    </div>
                </div>

                {(showDraftCompanion || showGhostWriter || showUserSettings) && (
                    <div
                        className={`w-4/12 bg-darkest-background rounded-xl mx-1 overflow-hidden flex flex-col transition-opacity duration-300 ${editorSettings.focusMode ? 'opacity-50 hover:opacity-100' : ''}`}>
                        <div className="flex-1 overflow-auto flex flex-col">
                            {showDraftCompanion && <DraftCompanion/>}
                            {showGhostWriter && <GhostWriter/>}
                            {showUserSettings && (
                                <UserEditorSettings
                                    settings={editorSettings}
                                    onSettingsChange={setEditorSettings}
                                />
                            )}
                        </div>
                    </div>
                )}
            </div>
        </div>
    );
}