'use client';
import React, {forwardRef, useContext, useEffect, useImperativeHandle, useState} from 'react';
import {
    initialSpellState,
    SpellEditState,
    SpellListItem,
    SpellListResponse,
    SpellProps,
    SpellPropsPost,
    SpellTagProps
} from "@/lib/models/Spell";
import {SessionContext} from "@/context/SessionContext";
import {AlertContext} from "@/context/AlertContext";
import {BookContext} from "@/context/BookContext";
import {LangContext, LangContextProps} from "@/context/LangContext";
import OfflineContext, {OfflineContextType} from "@/context/OfflineContext";
import {LocalSyncQueueContext, LocalSyncQueueContextProps} from "@/context/SyncQueueContext";
import {BooksSyncContext, BooksSyncContextProps} from "@/context/BooksSyncContext";
import {SyncedBook} from "@/lib/models/SyncedBook";
import System from '@/lib/models/System';
import {useTranslations} from "next-intl";
import ToggleSwitch from "@/components/form/ToggleSwitch";
import InputField from "@/components/form/InputField";
import {faToggleOn} from "@fortawesome/free-solid-svg-icons";
import SpellList from "@/components/book/settings/spells/SpellList";
import SpellDetail from "@/components/book/settings/spells/SpellDetail";
import SpellTagManager from "@/components/book/settings/spells/SpellTagManager";

interface SpellComponentProps {
    showToggle?: boolean;
}

export function SpellComponent(props: SpellComponentProps, ref: React.Ref<{ handleSave: () => Promise<void> }>) {
    const {showToggle = true} = props;
    const t = useTranslations();
    const {lang} = useContext<LangContextProps>(LangContext);
    const {isCurrentlyOffline} = useContext<OfflineContextType>(OfflineContext);
    const {addToQueue} = useContext<LocalSyncQueueContextProps>(LocalSyncQueueContext);
    const {localSyncedBooks} = useContext<BooksSyncContextProps>(BooksSyncContext);
    const {session} = useContext(SessionContext);
    const {book, setBook} = useContext(BookContext);
    const {errorMessage, successMessage} = useContext(AlertContext);

    const bookId: string | undefined = book?.bookId;
    const token: string = session.accessToken;
    
    const [spells, setSpells] = useState<SpellListItem[]>([]);
    const [tags, setTags] = useState<SpellTagProps[]>([]);
    const [selectedSpell, setSelectedSpell] = useState<SpellEditState | null>(null);
    const [toolEnabled, setToolEnabled] = useState<boolean>(book?.tools?.spells ?? false);
    const [showTagManager, setShowTagManager] = useState<boolean>(false);
    
    useImperativeHandle(ref, function () {
        return {
            handleSave: handleSaveSpell,
        };
    });
    
    useEffect((): void => {
        getSpells().then();
    }, []);
    
    async function handleToggleTool(enabled: boolean): Promise<void> {
        try {
            let response: boolean;
            if (isCurrentlyOffline() || book?.localBook) {
                response = await window.electron.invoke<boolean>('db:book:tool:update', {
                    bookId: bookId,
                    toolName: 'spells',
                    enabled: enabled
                });
            } else {
                response = await System.authPatchToServer<boolean>('book/tool-setting', {
                    bookId: bookId,
                    toolName: 'spells',
                    enabled: enabled
                }, token, lang);
                if (localSyncedBooks.find((syncedBook: SyncedBook): boolean => syncedBook.id === bookId)) {
                    addToQueue('db:book:tool:update', {
                        bookId: bookId,
                        toolName: 'spells',
                        enabled: enabled
                    });
                }
            }
            if (response && setBook && book) {
                setToolEnabled(enabled);
                setBook({
                    ...book, tools: {
                        characters: book.tools?.characters ?? false,
                        worlds: book.tools?.worlds ?? false,
                        locations: book.tools?.locations ?? false,
                        spells: enabled
                    }
                });
            }
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            }
        }
    }
    
    async function getSpells(): Promise<void> {
        try {
            let response: SpellListResponse;
            if (isCurrentlyOffline()) {
                response = await window.electron.invoke<SpellListResponse>('db:spell:list', {bookid: bookId});
            } else {
                if (book?.localBook) {
                    response = await window.electron.invoke<SpellListResponse>('db:spell:list', {bookid: bookId});
                } else {
                    response = await System.authGetQueryToServer<SpellListResponse>('spell/list', token, lang, {
                        bookid: bookId,
                    });
                }
            }
            if (response) {
                setSpells(response.spells);
                setTags(response.tags);
                setToolEnabled(response.enabled);
                if (setBook && book) {
                    setBook({
                        ...book, tools: {
                            characters: book.tools?.characters ?? false,
                            worlds: book.tools?.worlds ?? false,
                            locations: book.tools?.locations ?? false,
                            spells: response.enabled
                        }
                    });
                }
            }
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t("common.unknownError"));
            }
        }
    }
    
    async function handleSpellClick(spell: SpellListItem): Promise<void> {
        // Convertir les tags de SpellTagProps[] vers string[] (IDs)
        const tagIds: string[] = spell.tags.map((tag: SpellTagProps): string => tag.id);

        // D'abord afficher avec les données de la liste
        setSelectedSpell({
            id: spell.id,
            name: spell.name,
            description: spell.description,
            appearance: '',
            tags: tagIds,
            powerLevel: null,
            components: null,
            limitations: null,
            notes: null,
        });
        try {
            let response: SpellProps;
            if (isCurrentlyOffline()) {
                response = await window.electron.invoke<SpellProps>('db:spell:detail', {spellid: spell.id});
            } else {
                if (book?.localBook) {
                    response = await window.electron.invoke<SpellProps>('db:spell:detail', {spellid: spell.id});
                } else {
                    response = await System.authGetQueryToServer<SpellProps>('spell/detail', token, lang, {
                        spellid: spell.id,
                    });
                }
            }
            if (response) {
                setSelectedSpell((prev: SpellEditState | null): SpellEditState | null => {
                    if (!prev) return null;
                    return {
                        ...prev,
                        appearance: response.appearance,
                        powerLevel: response.powerLevel,
                        components: response.components,
                        limitations: response.limitations,
                        notes: response.notes,
                        // Garder les tags de la liste, pas ceux de l'API
                    };
                });
            }
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            }
        }
    }
    
    function handleAddSpell(): void {
        setSelectedSpell({...initialSpellState});
    }
    
    function handleSpellChange(key: keyof SpellEditState, value: string | string[] | null): void {
        if (selectedSpell) {
            setSelectedSpell({...selectedSpell, [key]: value});
        }
    }
    
    async function handleSaveSpell(): Promise<void> {
        if (selectedSpell) {
            if (selectedSpell.id === null) {
                await addNewSpell(selectedSpell);
            } else {
                await updateSpell(selectedSpell);
            }
        }
    }
    
    async function addNewSpell(spell: SpellEditState): Promise<void> {
        if (!spell.name) {
            errorMessage(t("spellComponent.errorNameRequired"));
            return;
        }
        if (!spell.description) {
            errorMessage(t("spellComponent.errorDescriptionRequired"));
            return;
        }
        if (!spell.appearance) {
            errorMessage(t("spellComponent.errorAppearanceRequired"));
            return;
        }
        try {
            const spellPost: SpellPropsPost = {
                name: spell.name,
                description: spell.description,
                appearance: spell.appearance,
                tags: spell.tags,
                powerLevel: spell.powerLevel,
                components: spell.components,
                limitations: spell.limitations,
                notes: spell.notes,
            };
            let spellId: string;
            if (isCurrentlyOffline() || book?.localBook) {
                spellId = await window.electron.invoke<string>('db:spell:create', {
                    bookId: bookId,
                    spell: spellPost,
                });
            } else {
                const createdSpell: SpellProps = await System.authPostToServer<SpellProps>('spell/add', {
                    bookId: bookId,
                    spell: spellPost,
                }, token, lang);
                if (!createdSpell || !createdSpell.id) {
                    errorMessage(t("spellComponent.errorAddSpell"));
                    return;
                }
                spellId = createdSpell.id;
                if (localSyncedBooks.find((syncedBook: SyncedBook): boolean => syncedBook.id === bookId)) {
                    addToQueue('db:spell:create', {
                        bookId: bookId,
                        spell: {...spellPost, id: spellId},
                    });
                }
            }
            if (!spellId) {
                errorMessage(t("spellComponent.errorAddSpell"));
                return;
            }
            // Ajouter à la liste avec les tags résolus
            const resolvedTags: SpellTagProps[] = tags.filter((tag: SpellTagProps) => spell.tags.includes(tag.id));
            const newSpellListItem: SpellListItem = {
                id: spellId,
                name: spell.name,
                description: spell.description.length > 150
                    ? spell.description.substring(0, 150) + '...'
                    : spell.description,
                tags: resolvedTags,
            };
            setSpells([...spells, newSpellListItem]);
            setSelectedSpell(null);
            successMessage(t("spellComponent.successAdd"));
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t("common.unknownError"));
            }
        }
    }
    
    async function updateSpell(spell: SpellEditState): Promise<void> {
        if (!spell.id) return;
        if (!spell.name) {
            errorMessage(t("spellComponent.errorNameRequired"));
            return;
        }
        if (!spell.description) {
            errorMessage(t("spellComponent.errorDescriptionRequired"));
            return;
        }
        if (!spell.appearance) {
            errorMessage(t("spellComponent.errorAppearanceRequired"));
            return;
        }
        try {
            const spellPost: SpellPropsPost = {
                name: spell.name,
                description: spell.description,
                appearance: spell.appearance,
                tags: spell.tags,
                powerLevel: spell.powerLevel,
                components: spell.components,
                limitations: spell.limitations,
                notes: spell.notes,
            };
            let response: boolean;
            if (isCurrentlyOffline() || book?.localBook) {
                response = await window.electron.invoke<boolean>('db:spell:update', {
                    spellId: spell.id,
                    spell: spellPost,
                });
            } else {
                response = await System.authPutToServer<boolean>('spell/update', {
                    spellId: spell.id,
                    spell: spellPost,
                }, token, lang);
                if (localSyncedBooks.find((syncedBook: SyncedBook): boolean => syncedBook.id === bookId)) {
                    addToQueue('db:spell:update', {
                        spellId: spell.id,
                        spell: spellPost,
                    });
                }
            }
            if (!response) {
                errorMessage(t("spellComponent.errorUpdateSpell"));
                return;
            }
            // Mettre à jour la liste avec les tags résolus
            const resolvedTags: SpellTagProps[] = tags.filter((tag: SpellTagProps) => spell.tags.includes(tag.id));
            setSpells(spells.map((s: SpellListItem): SpellListItem =>
                s.id === spell.id ? {
                    id: spell.id,
                    name: spell.name,
                    description: spell.description.length > 150
                        ? spell.description.substring(0, 150) + '...'
                        : spell.description,
                    tags: resolvedTags,
                } : s
            ));
            setSelectedSpell(null);
            successMessage(t("spellComponent.successUpdate"));
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t("common.unknownError"));
            }
        }
    }
    
    async function handleDeleteSpell(spellId: string): Promise<void> {
        try {
            let response: boolean;
            if (isCurrentlyOffline() || book?.localBook) {
                response = await window.electron.invoke<boolean>('db:spell:delete', {
                    spellId: spellId,
                });
            } else {
                response = await System.authDeleteToServer<boolean>('spell/delete', {
                    spellId: spellId,
                }, token, lang);
                if (localSyncedBooks.find((syncedBook: SyncedBook): boolean => syncedBook.id === bookId)) {
                    addToQueue('db:spell:delete', {
                        spellId: spellId,
                    });
                }
            }
            if (!response) {
                errorMessage(t("spellComponent.errorDeleteSpell"));
                return;
            }
            setSpells(spells.filter((s: SpellListItem) => s.id !== spellId));
            setSelectedSpell(null);
            successMessage(t("spellComponent.successDelete"));
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t("common.unknownError"));
            }
        }
    }
    
    function handleManageTags(): void {
        setShowTagManager(true);
    }
    
    function handleBackFromTagManager(): void {
        setShowTagManager(false);
    }
    
    async function handleCreateTag(name: string, color: string): Promise<SpellTagProps | null> {
        try {
            let tagId: string;
            if (isCurrentlyOffline() || book?.localBook) {
                tagId = await window.electron.invoke<string>('db:spell:tag:create', {
                    bookId: bookId,
                    name: name,
                    color: color,
                });
            } else {
                const newTag: SpellTagProps = await System.authPostToServer<SpellTagProps>('spell/tag/add', {
                    bookId: bookId,
                    name: name,
                    color: color,
                }, token, lang);
                if (!newTag || !newTag.id) {
                    return null;
                }
                tagId = newTag.id;
                if (localSyncedBooks.find((syncedBook: SyncedBook): boolean => syncedBook.id === bookId)) {
                    addToQueue('db:spell:tag:create', {
                        bookId: bookId,
                        name: name,
                        color: color,
                        tagId: tagId,
                    });
                }
            }
            if (tagId) {
                const createdTag: SpellTagProps = {id: tagId, name: name, color: color};
                setTags([...tags, createdTag]);
                return createdTag;
            }
            return null;
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            }
            return null;
        }
    }
    
    async function handleUpdateTag(tagId: string, name: string, color: string): Promise<boolean> {
        try {
            let response: boolean;
            if (isCurrentlyOffline() || book?.localBook) {
                response = await window.electron.invoke<boolean>('db:spell:tag:update', {
                    tagId: tagId,
                    name: name,
                    color: color,
                });
            } else {
                response = await System.authPutToServer<boolean>('spell/tag/update', {
                    tagId: tagId,
                    name: name,
                    color: color,
                }, token, lang);
                if (localSyncedBooks.find((syncedBook: SyncedBook): boolean => syncedBook.id === bookId)) {
                    addToQueue('db:spell:tag:update', {
                        tagId: tagId,
                        name: name,
                        color: color,
                    });
                }
            }
            if (response) {
                setTags(tags.map((tag: SpellTagProps): SpellTagProps =>
                    tag.id === tagId ? {id: tagId, name: name, color: color} : tag
                ));
                return true;
            }
            return false;
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            }
            return false;
        }
    }
    
    async function handleDeleteTag(tagId: string): Promise<boolean> {
        try {
            let response: boolean;
            if (isCurrentlyOffline() || book?.localBook) {
                response = await window.electron.invoke<boolean>('db:spell:tag:delete', {
                    tagId: tagId,
                    bookId: bookId,
                });
            } else {
                response = await System.authDeleteToServer<boolean>('spell/tag/delete', {
                    tagId: tagId,
                    bookId: bookId,
                }, token, lang);
                if (localSyncedBooks.find((syncedBook: SyncedBook): boolean => syncedBook.id === bookId)) {
                    addToQueue('db:spell:tag:delete', {
                        tagId: tagId,
                        bookId: bookId,
                    });
                }
            }
            if (response) {
                setTags(tags.filter((tag: SpellTagProps): boolean => tag.id !== tagId));
                return true;
            }
            return false;
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            }
            return false;
        }
    }
    
    return (
        <div className="space-y-5">
            {showToggle && (
                <div className="bg-secondary/20 rounded-xl p-5 shadow-inner border border-secondary/30">
                    <InputField
                        icon={faToggleOn}
                        fieldName={t('spellComponent.enableTool')}
                        input={
                            <ToggleSwitch
                                checked={toolEnabled}
                                onChange={async (checked: boolean): Promise<void> => handleToggleTool(checked)}
                            />
                        }
                    />
                    <p className="text-muted text-sm mt-2">
                        {t('spellComponent.enableToolDescription')}
                    </p>
                </div>
            )}
            {toolEnabled && (
                <>
                    {showTagManager ? (
                        <SpellTagManager
                            tags={tags}
                            onBack={handleBackFromTagManager}
                            onCreateTag={handleCreateTag}
                            onUpdateTag={handleUpdateTag}
                            onDeleteTag={handleDeleteTag}
                        />
                    ) : selectedSpell ? (
                        <SpellDetail
                            selectedSpell={selectedSpell}
                            setSelectedSpell={setSelectedSpell}
                            availableTags={tags}
                            handleSpellChange={handleSpellChange}
                            handleSaveSpell={handleSaveSpell}
                            handleDeleteSpell={handleDeleteSpell}
                            handleCreateTagInline={handleCreateTag}
                        />
                    ) : (
                        <SpellList
                            spells={spells}
                            tags={tags}
                            handleSpellClick={handleSpellClick}
                            handleAddSpell={handleAddSpell}
                            handleManageTags={handleManageTags}
                        />
                    )}
                </>
            )}
        </div>
    );
}

export default forwardRef(SpellComponent);
