'use client';
import {Dispatch, forwardRef, SetStateAction, useContext, useEffect, useImperativeHandle, useState} from 'react';
import {Attribute, CharacterProps} from "@/lib/models/Character";
import {SessionContext} from "@/context/SessionContext";
import CharacterList from './CharacterList';
import System from '@/lib/models/System';
import {AlertContext} from "@/context/AlertContext";
import {BookContext} from "@/context/BookContext";
import CharacterDetail from "@/components/book/settings/characters/CharacterDetail";
import {useTranslations} from "next-intl";
import {LangContext, LangContextProps} from "@/context/LangContext";
import OfflineContext, {OfflineContextType} from "@/context/OfflineContext";

interface CharacterDetailProps {
    selectedCharacter: CharacterProps | null;
    setSelectedCharacter: Dispatch<SetStateAction<CharacterProps | null>>;
    handleCharacterChange: (key: keyof CharacterProps, value: string) => void;
    handleAddElement: (section: keyof CharacterProps, element: any) => void;
    handleRemoveElement: (
        section: keyof CharacterProps,
        index: number,
        attrId: string,
    ) => void;
    handleSaveCharacter: () => void;
}

const initialCharacterState: CharacterProps = {
    id: null,
    name: '',
    lastName: '',
    category: 'none',
    title: '',
    role: '',
    image: 'https://via.placeholder.com/150',
    biography: '',
    history: '',
    physical: [],
    psychological: [],
    relations: [],
    skills: [],
    weaknesses: [],
    strengths: [],
    goals: [],
    motivations: [],
};

export function CharacterComponent(props: any, ref: any) {
    const t = useTranslations();
    const {lang} = useContext<LangContextProps>(LangContext)
    const {isCurrentlyOffline} = useContext<OfflineContextType>(OfflineContext);
    const {session} = useContext(SessionContext);
    const {book} = useContext(BookContext);
    const {errorMessage, successMessage} = useContext(AlertContext);
    const [characters, setCharacters] = useState<CharacterProps[]>([]);
    const [selectedCharacter, setSelectedCharacter] = useState<CharacterProps | null>(null);
    
    useImperativeHandle(ref, function () {
        return {
            handleSave: handleSaveCharacter,
        };
    });
    
    useEffect((): void => {
        getCharacters().then();
    }, []);
    
    async function getCharacters(): Promise<void> {
        try {
            let response: CharacterProps[];
            if (isCurrentlyOffline()) {
                response = await window.electron.invoke<CharacterProps[]>('db:character:list', {bookid: book?.bookId});
            } else {
                response = await System.authGetQueryToServer<CharacterProps[]>(`character/list`, session.accessToken, lang, {
                    bookid: book?.bookId,
                });
            }
            if (response) {
                setCharacters(response);
            }
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t("common.unknownError"));
            }
        }
    }
    
    function handleCharacterClick(character: CharacterProps): void {
        setSelectedCharacter({...character});
    }
    
    function handleAddCharacter(): void {
        setSelectedCharacter({...initialCharacterState});
    }
    
    async function handleSaveCharacter(): Promise<void> {
        if (selectedCharacter) {
            const updatedCharacter: CharacterProps = {...selectedCharacter};
            if (selectedCharacter.id === null) {
                await addNewCharacter(updatedCharacter);
            } else {
                await updateCharacter(updatedCharacter);
            }
        }
    }
    
    async function addNewCharacter(updatedCharacter: CharacterProps): Promise<void> {
        if (!updatedCharacter.name) {
            errorMessage(t("characterComponent.errorNameRequired"));
            return;
        }
        if (updatedCharacter.category === 'none') {
            errorMessage(t("characterComponent.errorCategoryRequired"));
            return;
        }
        try {
            let characterId: string;
            if (isCurrentlyOffline()) {
                characterId = await window.electron.invoke<string>('db:character:create', {
                    bookId: book?.bookId,
                    character: updatedCharacter,
                });
            } else {
                characterId = await System.authPostToServer<string>(`character/add`, {
                    bookId: book?.bookId,
                    character: updatedCharacter,
                }, session.accessToken, lang);
            }
            if (!characterId) {
                errorMessage(t("characterComponent.errorAddCharacter"));
                return;
            }
            updatedCharacter.id = characterId;
            setCharacters([...characters, updatedCharacter]);
            setSelectedCharacter(null);
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t("common.unknownError"));
            }
        }
    }
    
    async function updateCharacter(updatedCharacter: CharacterProps,): Promise<void> {
        try {
            let response: boolean;
            if (isCurrentlyOffline()) {
                response = await window.electron.invoke<boolean>('db:character:update', {
                    character: updatedCharacter,
                });
            } else {
                response = await System.authPostToServer<boolean>(`character/update`, {
                    character: updatedCharacter,
                }, session.accessToken, lang);
            }
            if (!response) {
                errorMessage(t("characterComponent.errorUpdateCharacter"));
                return;
            }
            setCharacters(
                characters.map((char: CharacterProps): CharacterProps =>
                    char.id === updatedCharacter.id ? updatedCharacter : char,
                ),
            );
            setSelectedCharacter(null);
            successMessage(t("characterComponent.successUpdate"));
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t("common.unknownError"));
            }
        }
    }
    
    function handleCharacterChange(
        key: keyof CharacterProps,
        value: string,
    ): void {
        if (selectedCharacter) {
            setSelectedCharacter({...selectedCharacter, [key]: value});
        }
    }
    
    async function handleAddElement(
        section: keyof CharacterProps,
        value: Attribute,
    ): Promise<void> {
        if (selectedCharacter) {
            if (selectedCharacter.id === null) {
                const updatedSection: any[] = [
                    ...(selectedCharacter[section] as any[]),
                    value,
                ];
                setSelectedCharacter({...selectedCharacter, [section]: updatedSection});
            } else {
                try {
                    let attributeId: string;
                    if (isCurrentlyOffline()) {
                        attributeId = await window.electron.invoke<string>('db:character:attribute:add', {
                            characterId: selectedCharacter.id,
                            type: section,
                            name: value.name,
                        });
                    } else {
                        attributeId = await System.authPostToServer<string>(`character/attribute/add`, {
                            characterId: selectedCharacter.id,
                            type: section,
                            name: value.name,
                        }, session.accessToken, lang);
                    }
                    if (!attributeId) {
                        errorMessage(t("characterComponent.errorAddAttribute"));
                        return;
                    }
                    const newValue: Attribute = {
                        name: value.name,
                        id: attributeId,
                    };
                    const updatedSection: Attribute[] = [...(selectedCharacter[section] as Attribute[]), newValue,];
                    setSelectedCharacter({...selectedCharacter, [section]: updatedSection});
                } catch (e: unknown) {
                    if (e instanceof Error) {
                        errorMessage(e.message);
                    } else {
                        errorMessage(t("common.unknownError"));
                    }
                }
            }
        }
    }
    
    async function handleRemoveElement(
        section: keyof CharacterProps,
        index: number,
        attrId: string,
    ): Promise<void> {
        if (selectedCharacter) {
            if (selectedCharacter.id === null) {
                const updatedSection: Attribute[] = (
                    selectedCharacter[section] as Attribute[]
                ).filter((_, i: number): boolean => i !== index);
                setSelectedCharacter({...selectedCharacter, [section]: updatedSection});
            } else {
                try {
                    let response: boolean;
                    if (isCurrentlyOffline()) {
                        response = await window.electron.invoke<boolean>('db:character:attribute:delete', {
                            attributeId: attrId,
                        });
                    } else {
                        response = await System.authDeleteToServer<boolean>(`character/attribute/delete`, {
                            attributeId: attrId,
                        }, session.accessToken, lang);
                    }
                    if (!response) {
                        errorMessage(t("characterComponent.errorRemoveAttribute"));
                        return;
                    }
                    const updatedSection: Attribute[] = (
                        selectedCharacter[section] as Attribute[]
                    ).filter((_, i: number): boolean => i !== index);
                    setSelectedCharacter({
                        ...selectedCharacter,
                        [section]: updatedSection,
                    });
                } catch (e: unknown) {
                    if (e instanceof Error) {
                        errorMessage(e.message);
                    } else {
                        errorMessage(t("common.unknownError"));
                    }
                }
            }
        }
    }
    
    return (
        <div className="space-y-5">
            {selectedCharacter ? (
                <CharacterDetail
                    selectedCharacter={selectedCharacter}
                    setSelectedCharacter={setSelectedCharacter}
                    handleAddElement={handleAddElement}
                    handleRemoveElement={handleRemoveElement}
                    handleCharacterChange={handleCharacterChange}
                    handleSaveCharacter={handleSaveCharacter}
                />
            ) : (
                <CharacterList
                    characters={characters}
                    handleAddCharacter={handleAddCharacter}
                    handleCharacterClick={handleCharacterClick}
                />
            )}
        </div>
    );
}

export default forwardRef(CharacterComponent);