import {ChangeEvent, useContext, useEffect, useState} from "react";
import {Editor, EditorContent, useEditor} from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import Underline from "@tiptap/extension-underline";
import TextAlign from "@tiptap/extension-text-align";
import System from "@/lib/models/System";
import {ChapterContext} from "@/context/ChapterContext";
import {BookContext} from "@/context/BookContext";
import {SelectBoxProps} from "@/shared/interface";
import {AlertContext} from "@/context/AlertContext";
import {SessionContext} from "@/context/SessionContext";
import {
    faCubes,
    faFeather,
    faGlobe,
    faMagicWandSparkles,
    faMapPin,
    faPalette,
    faUser
} from "@fortawesome/free-solid-svg-icons";
import SubmitButtonWLoading from "@/components/form/SubmitButtonWLoading";
import QSTextGeneratedPreview from "@/components/QSTextGeneratedPreview";
import {EditorContext} from "@/context/EditorContext";
import {useTranslations} from "next-intl";
import QuillSense from "@/lib/models/QuillSense";
import TextInput from "@/components/form/TextInput";
import InputField from "@/components/form/InputField";
import TexteAreaInput from "@/components/form/TexteAreaInput";
import SuggestFieldInput from "@/components/form/SuggestFieldInput";
import Collapse from "@/components/Collapse";
import {LangContext, LangContextProps} from "@/context/LangContext";
import {BookTags} from "@/lib/models/Book";
import {AIUsageContext, AIUsageContextProps} from "@/context/AIUsageContext";
import {configs} from "@/lib/configs";
import OfflineContext, {OfflineContextType} from "@/context/OfflineContext";
import AdvancedGenerationOptions from "@/components/form/AdvancedGenerationOptions";

interface CompanionContent {
    version: number;
    content: string;
    wordsCount: number;
}

export default function DraftCompanion() {
    const t = useTranslations();
    const {setTotalPrice, setTotalCredits} = useContext<AIUsageContextProps>(AIUsageContext)
    const {lang} = useContext<LangContextProps>(LangContext)
    const {isCurrentlyOffline} = useContext<OfflineContextType>(OfflineContext);

    const mainEditor: Editor | null = useEditor({
        extensions: [
            StarterKit,
            Underline,
            TextAlign.configure({
                types: ['heading', 'paragraph'],
            }),
        ],
        injectCSS: false,
        editable: false,
        immediatelyRender: false,
    });
    const {editor} = useContext(EditorContext);
    const {chapter} = useContext(ChapterContext);
    const {book} = useContext(BookContext);
    const {session} = useContext(SessionContext);
    const {errorMessage, infoMessage} = useContext(AlertContext);

    const [draftVersion, setDraftVersion] = useState<number>(0);
    const [draftWordCount, setDraftWordCount] = useState<number>(0);
    const [refinedText, setRefinedText] = useState<string>('');
    const [isRefining, setIsRefining] = useState<boolean>(false);
    const [showRefinedText, setShowRefinedText] = useState<boolean>(false);
    const [showEnhancer, setShowEnhancer] = useState<boolean>(false);
    const [abortController, setAbortController] = useState<ReadableStreamDefaultReader<Uint8Array> | null>(null);
    
    const [toneAtmosphere, setToneAtmosphere] = useState<string>('');
    const [specifications, setSpecifications] = useState<string>('');
    
    const [characters, setCharacters] = useState<SelectBoxProps[]>([]);
    const [locations, setLocations] = useState<SelectBoxProps[]>([]);
    const [objects, setObjects] = useState<SelectBoxProps[]>([]);
    const [worldElements, setWorldElements] = useState<SelectBoxProps[]>([]);
    
    const [taguedCharacters, setTaguedCharacters] = useState<string[]>([]);
    const [taguedLocations, setTaguedLocations] = useState<string[]>([]);
    const [taguedObjects, setTaguedObjects] = useState<string[]>([]);
    const [taguedWorldElements, setTaguedWorldElements] = useState<string[]>([]);
    
    const [searchCharacters, setSearchCharacters] = useState<string>('');
    const [searchLocations, setSearchLocations] = useState<string>('');
    const [searchObjects, setSearchObjects] = useState<string>('');
    const [searchWorldElements, setSearchWorldElements] = useState<string>('');
    
    const [showCharacterSuggestions, setShowCharacterSuggestions] = useState<boolean>(false);
    const [showLocationSuggestions, setShowLocationSuggestions] = useState<boolean>(false);
    const [showObjectSuggestions, setShowObjectSuggestions] = useState<boolean>(false);
    const [showWorldElementSuggestions, setShowWorldElementSuggestions] = useState<boolean>(false);
    
    const [useExplicit, setUseExplicit] = useState<boolean>(false);
    const [useSmart, setUseSmart] = useState<boolean>(false);
    
    const isGPTEnabled: boolean = QuillSense.isOpenAIEnabled(session);
    const isSubTierTree: boolean = QuillSense.getSubLevel(session) === 3;
    const hasAccess: boolean = (isGPTEnabled || isSubTierTree) && !isCurrentlyOffline() && !book?.localBook;
    
    useEffect((): void => {
        getDraftContent().then();
        if (showEnhancer) {
            fetchTags().then();
        }
    }, [mainEditor, chapter, showEnhancer]);
    
    async function getDraftContent(): Promise<void> {
        try {
            let response: CompanionContent | null;
            if (isCurrentlyOffline()) {
                response = await window.electron.invoke<CompanionContent>('db:chapter:content:companion', {
                    bookid: book?.bookId,
                    chapterid: chapter?.chapterId,
                    version: chapter?.chapterContent.version,
                });
            } else {
                if (book?.localBook) {
                    response = await window.electron.invoke<CompanionContent>('db:chapter:content:companion', {
                        bookid: book?.bookId,
                        chapterid: chapter?.chapterId,
                        version: chapter?.chapterContent.version,
                    });
                } else {
                    response = await System.authGetQueryToServer<CompanionContent>(`chapter/content/companion`, session.accessToken, lang, {
                        bookid: book?.bookId,
                        chapterid: chapter?.chapterId,
                        version: chapter?.chapterContent.version,
                    });
                }
            }
            if (response && mainEditor) {
                mainEditor.commands.setContent(JSON.parse(response.content));
                setDraftVersion(response.version);
                setDraftWordCount(response.wordsCount);
            } else if (response && response.content.length === 0 && mainEditor) {
                mainEditor.commands.setContent({
                    "type": "doc",
                    "content": [
                        {
                            "type": "heading",
                            "attrs": {
                                "level": 1
                            },
                            "content": [
                                {
                                    "type": "text",
                                    "text": t("draftCompanion.noPreviousVersion")
                                }
                            ]
                        }
                    ]
                });
            }
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t("draftCompanion.unknownError"));
            }
        }
    }
    
    async function fetchTags(): Promise<void> {
        try {
            let responseTags: BookTags | null;
            if (isCurrentlyOffline()) {
                responseTags = await window.electron.invoke<BookTags>('db:book:tags', book?.bookId);
            } else {
                if (book?.localBook) {
                    responseTags = await window.electron.invoke<BookTags>('db:book:tags', book?.bookId);
                } else {
                    responseTags = await System.authGetQueryToServer<BookTags>(`book/tags`, session.accessToken, lang, {
                        bookId: book?.bookId
                    });
                }
            }
            if (responseTags) {
                setCharacters(responseTags.characters);
                setLocations(responseTags.locations);
                setObjects(responseTags.objects);
                setWorldElements(responseTags.worldElements);
            }
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t("draftCompanion.unknownError"));
            }
        }
    }
    
    async function handleStopRefining(): Promise<void> {
        if (abortController) {
            await abortController.cancel();
            setAbortController(null);
            infoMessage(t("draftCompanion.abortSuccess"));
        }
    }
    
    async function handleQuillSenseRefined(): Promise<void> {
        if (chapter && session?.accessToken) {
            setIsRefining(true);
            setShowRefinedText(false);
            setRefinedText('');
            
            try {
                const response: Response = await fetch(`${configs.apiUrl}quillsense/refine`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': `Bearer ${session.accessToken}`,
                    },
                    body: JSON.stringify({
                        chapterId: chapter?.chapterId,
                        bookId: book?.bookId,
                        toneAndAtmosphere: toneAtmosphere,
                        advancedPrompt: specifications,
                        tags: {
                            characters: taguedCharacters,
                            locations: taguedLocations,
                            objects: taguedObjects,
                            worldElements: taguedWorldElements,
                        },
                        useExplicit: useExplicit,
                        useSmart: useSmart,
                    }),
                });
                
                if (!response.ok) {
                    const error: { message?: string } = await response.json();
                    errorMessage(error.message || t('draftCompanion.errorRefineDraft'));
                    setIsRefining(false);
                    return;
                }
                
                const reader: ReadableStreamDefaultReader<Uint8Array> | undefined = response.body?.getReader();
                const decoder: TextDecoder = new TextDecoder();
                let accumulatedText: string = '';
                
                if (!reader) {
                    errorMessage(t('draftCompanion.errorRefineDraft'));
                    setIsRefining(false);
                    return;
                }
                
                setAbortController(reader);
                
                while (true) {
                    try {
                        const {done, value}: ReadableStreamReadResult<Uint8Array> = await reader.read();
                        
                        if (done) break;
                        
                        const chunk: string = decoder.decode(value, {stream: true});
                        const lines: string[] = chunk.split('\n');
                        
                        for (const line of lines) {
                            if (line.startsWith('data: ')) {
                                try {
                                    const dataStr: string = line.slice(6);
                                    const data: {
                                        content?: string;
                                        totalPrice?: number;
                                        useYourKey?: boolean;
                                    } = JSON.parse(dataStr);
                                    
                                    if ('content' in data && data.content) {
                                        accumulatedText += data.content;
                                        setRefinedText(accumulatedText);
                                    }
                                    
                                    else if ('useYourKey' in data && 'totalPrice' in data) {
                                        if (data.useYourKey) {
                                            setTotalPrice((prev: number): number => prev + data.totalPrice!);
                                        } else {
                                            setTotalCredits(data.totalPrice!);
                                        }
                                    }
                                } catch (e: unknown) {
                                    console.error('Error parsing SSE data:', e);
                                }
                            }
                        }
                    } catch (e: unknown) {
                        break;
                    }
                }
                
                setIsRefining(false);
                setShowRefinedText(true);
                setAbortController(null);
            } catch (e: unknown) {
                setIsRefining(false);
                setAbortController(null);
                if (e instanceof Error) {
                    errorMessage(e.message);
                } else {
                    errorMessage(t('draftCompanion.unknownErrorRefineDraft'));
                }
            }
        }
    }
    
    function insertText(): void {
        if (editor && refinedText) {
            editor.commands.focus('end');
            if (editor.getText().length > 0) {
                editor.commands.insertContent('\n\n');
            }
            editor.commands.insertContent(System.textContentToHtml(refinedText));
            setShowRefinedText(false);
        }
    }
    
    function filteredCharacters(): SelectBoxProps[] {
        if (searchCharacters.trim().length === 0) return [];
        return characters
            .filter((item: SelectBoxProps): boolean => item.label.toLowerCase().includes(searchCharacters.toLowerCase()) && !taguedCharacters.includes(item.value))
            .slice(0, 3);
    }
    
    function filteredLocations(): SelectBoxProps[] {
        if (searchLocations.trim().length === 0) return [];
        return locations
            .filter((item: SelectBoxProps): boolean => item.label.toLowerCase().includes(searchLocations.toLowerCase()) && !taguedLocations.includes(item.value))
            .slice(0, 3);
    }
    
    function filteredObjects(): SelectBoxProps[] {
        if (searchObjects.trim().length === 0) return [];
        return objects
            .filter((item: SelectBoxProps): boolean => item.label.toLowerCase().includes(searchObjects.toLowerCase()) && !taguedObjects.includes(item.value))
            .slice(0, 3);
    }
    
    function filteredWorldElements(): SelectBoxProps[] {
        if (searchWorldElements.trim().length === 0) return [];
        return worldElements
            .filter((item: SelectBoxProps): boolean => item.label.toLowerCase().includes(searchWorldElements.toLowerCase()) && !taguedWorldElements.includes(item.value))
            .slice(0, 3);
    }
    
    function handleAddCharacter(value: string): void {
        if (!taguedCharacters.includes(value)) {
            const newCharacters: string[] = [...taguedCharacters, value];
            setTaguedCharacters(newCharacters);
        }
        setSearchCharacters('');
        setShowCharacterSuggestions(false);
    }
    
    function handleAddLocation(value: string): void {
        if (!taguedLocations.includes(value)) {
            const newLocations: string[] = [...taguedLocations, value];
            setTaguedLocations(newLocations);
        }
        setSearchLocations('');
        setShowLocationSuggestions(false);
    }
    
    function handleAddObject(value: string): void {
        if (!taguedObjects.includes(value)) {
            const newObjects: string[] = [...taguedObjects, value];
            setTaguedObjects(newObjects);
        }
        setSearchObjects('');
        setShowObjectSuggestions(false);
    }
    
    function handleAddWorldElement(value: string): void {
        if (!taguedWorldElements.includes(value)) {
            const newWorldElements: string[] = [...taguedWorldElements, value];
            setTaguedWorldElements(newWorldElements);
        }
        setSearchWorldElements('');
        setShowWorldElementSuggestions(false);
    }
    
    function handleRemoveCharacter(value: string): void {
        setTaguedCharacters(taguedCharacters.filter((tag: string): boolean => tag !== value));
    }
    
    function handleRemoveLocation(value: string): void {
        setTaguedLocations(taguedLocations.filter((tag: string): boolean => tag !== value));
    }
    
    function handleRemoveObject(value: string): void {
        setTaguedObjects(taguedObjects.filter((tag: string): boolean => tag !== value));
    }
    
    function handleRemoveWorldElement(value: string): void {
        setTaguedWorldElements(taguedWorldElements.filter((tag: string): boolean => tag !== value));
    }
    
    function handleCharacterSearch(text: string): void {
        setSearchCharacters(text);
        setShowCharacterSuggestions(text.trim().length > 0);
    }
    
    function handleLocationSearch(text: string): void {
        setSearchLocations(text);
        setShowLocationSuggestions(text.trim().length > 0);
    }
    
    function handleObjectSearch(text: string): void {
        setSearchObjects(text);
        setShowObjectSuggestions(text.trim().length > 0);
    }
    
    function handleWorldElementSearch(text: string): void {
        setSearchWorldElements(text);
        setShowWorldElementSuggestions(text.trim().length > 0);
    }
    
    function getCharacterLabel(value: string): string {
        const character: SelectBoxProps | undefined = characters.find((item: SelectBoxProps): boolean => item.value === value);
        return character ? character.label : value;
    }
    
    function getLocationLabel(value: string): string {
        const location: SelectBoxProps | undefined = locations.find((item: SelectBoxProps): boolean => item.value === value);
        return location ? location.label : value;
    }
    
    function getObjectLabel(value: string): string {
        const object: SelectBoxProps | undefined = objects.find((item: SelectBoxProps): boolean => item.value === value);
        return object ? object.label : value;
    }
    
    function getWorldElementLabel(value: string): string {
        const element: SelectBoxProps | undefined = worldElements.find((item: SelectBoxProps): boolean => item.value === value);
        return element ? element.label : value;
    }
    
    if (showEnhancer && book?.quillsenseEnabled !== false) {
        return (
            <div className="flex flex-col h-full min-h-0 overflow-hidden">
                <div
                    className="flex items-center justify-between p-4 bg-secondary/30 backdrop-blur-sm border-b border-secondary/50 flex-shrink-0 shadow-sm">
                    <h2 className="text-text-primary font-['ADLaM_Display'] text-xl">Amélioration de texte</h2>
                    <button
                        onClick={(): void => setShowEnhancer(false)}
                        className="px-5 py-2.5 bg-secondary/50 hover:bg-secondary text-text-primary rounded-xl transition-all duration-200 hover:scale-105 shadow-md border border-secondary/50 font-medium"
                    >
                        Retour
                    </button>
                </div>
                
                <div className="flex-1 overflow-y-auto p-4 space-y-4 custom-scrollbar">
                    <Collapse
                        title="Style d'écriture"
                        content={
                            <div className="space-y-4">
                                <InputField
                                    icon={faPalette}
                                    fieldName={t("ghostWriter.toneAtmosphere")}
                                    input={
                                        <TextInput
                                            value={toneAtmosphere}
                                            setValue={(e: ChangeEvent<HTMLInputElement>) => setToneAtmosphere(e.target.value)}
                                            placeholder={t("ghostWriter.tonePlaceholder")}
                                        />
                                    }
                                />
                            </div>
                        }
                    />
                    
                    <Collapse
                        title="Tags contextuels"
                        content={
                            <div className="space-y-4">
                                <SuggestFieldInput inputFieldName={`Personnages`}
                                                   inputFieldIcon={faUser}
                                                   searchTags={searchCharacters}
                                                   tagued={taguedCharacters}
                                                   handleTagSearch={(e) => handleCharacterSearch(e.target.value)}
                                                   handleAddTag={handleAddCharacter}
                                                   handleRemoveTag={handleRemoveCharacter}
                                                   filteredTags={filteredCharacters}
                                                   showTagSuggestions={showCharacterSuggestions}
                                                   setShowTagSuggestions={setShowCharacterSuggestions}
                                                   getTagLabel={getCharacterLabel}
                                />
                                
                                <SuggestFieldInput inputFieldName={`Lieux`}
                                                   inputFieldIcon={faMapPin}
                                                   searchTags={searchLocations}
                                                   tagued={taguedLocations}
                                                   handleTagSearch={(e) => handleLocationSearch(e.target.value)}
                                                   handleAddTag={handleAddLocation}
                                                   handleRemoveTag={handleRemoveLocation}
                                                   filteredTags={filteredLocations}
                                                   showTagSuggestions={showLocationSuggestions}
                                                   setShowTagSuggestions={setShowLocationSuggestions}
                                                   getTagLabel={getLocationLabel}
                                />
                                
                                <SuggestFieldInput inputFieldName={`Objets`}
                                                   inputFieldIcon={faCubes}
                                                   searchTags={searchObjects}
                                                   tagued={taguedObjects}
                                                   handleTagSearch={(e) => handleObjectSearch(e.target.value)}
                                                   handleAddTag={handleAddObject}
                                                   handleRemoveTag={handleRemoveObject}
                                                   filteredTags={filteredObjects}
                                                   showTagSuggestions={showObjectSuggestions}
                                                   setShowTagSuggestions={setShowObjectSuggestions}
                                                   getTagLabel={getObjectLabel}
                                />
                                
                                <SuggestFieldInput inputFieldName={`Éléments mondiaux`}
                                                   inputFieldIcon={faGlobe}
                                                   searchTags={searchWorldElements}
                                                   tagued={taguedWorldElements}
                                                   handleTagSearch={(e) => handleWorldElementSearch(e.target.value)}
                                                   handleAddTag={handleAddWorldElement}
                                                   handleRemoveTag={handleRemoveWorldElement}
                                                   filteredTags={filteredWorldElements}
                                                   showTagSuggestions={showWorldElementSuggestions}
                                                   setShowTagSuggestions={setShowWorldElementSuggestions}
                                                   getTagLabel={getWorldElementLabel}
                                />
                            </div>
                        }
                    />
                    
                    <div className="bg-secondary/20 rounded-xl p-5 shadow-inner border border-secondary/30">
                        <InputField
                            icon={faMagicWandSparkles}
                            fieldName="Spécifications"
                            input={
                                <TexteAreaInput
                                    value={specifications}
                                    setValue={(e: ChangeEvent<HTMLTextAreaElement>) => setSpecifications(e.target.value)}
                                    placeholder="Spécifications particulières pour l'amélioration..."
                                    maxLength={600}
                                />
                            }
                        />
                    </div>
                    
                    <AdvancedGenerationOptions
                        useExplicit={useExplicit}
                        setUseExplicit={setUseExplicit}
                        useSmart={useSmart}
                        setUseSmart={setUseSmart}
                    />
                </div>
                
                <div
                    className="p-5 border-t border-secondary/50 bg-secondary/30 backdrop-blur-sm shrink-0 shadow-inner">
                    <div className="flex justify-center">
                        <SubmitButtonWLoading
                            callBackAction={handleQuillSenseRefined}
                            isLoading={isRefining}
                            text={t("draftCompanion.refine")}
                            loadingText={t("draftCompanion.refining")}
                            icon={faMagicWandSparkles}
                        />
                    </div>
                </div>
                {(showRefinedText || isRefining) && (
                    <QSTextGeneratedPreview
                        onClose={(): void => setShowRefinedText(false)}
                        onRefresh={handleQuillSenseRefined}
                        value={refinedText}
                        onInsert={insertText}
                        isGenerating={isRefining}
                        onStop={handleStopRefining}
                    />
                )}
            </div>
        );
    }
    
    return (
        <div className="flex flex-col h-full min-h-0 overflow-hidden font-['Lora']">
            <div
                className="flex items-center justify-between p-4 bg-secondary/30 backdrop-blur-sm border-b border-secondary/50 flex-shrink-0 font-['ADLaM_Display'] shadow-sm">
                <div className="mr-4 text-primary-light">
                    <span>{t("draftCompanion.words")}: </span>
                    <span className="text-text-primary">{draftWordCount}</span>
                </div>
                {
                    hasAccess && book?.quillsenseEnabled !== false && chapter?.chapterContent.version === 3 && (
                        <div className="flex gap-2">
                            <SubmitButtonWLoading
                                callBackAction={(): void => setShowEnhancer(true)}
                                isLoading={isRefining}
                                text={t("draftCompanion.refine")}
                                loadingText={t("draftCompanion.refining")}
                                icon={faFeather}
                            />
                        </div>
                    )
                }
            </div>
            <div className="flex-1 min-h-0 overflow-auto">
                <EditorContent
                    className="w-full h-full tiptap-draft"
                    editor={mainEditor}
                />
            </div>
        </div>
    );
}