'use client';

import {ChangeEvent, useContext, useState} from "react";
import {WorldContext} from "@/context/WorldContext";
import TextInput from "@/components/form/TextInput";
import TexteAreaInput from "@/components/form/TexteAreaInput";
import {WorldElement, WorldProps} from "@/lib/models/World";
import {AlertContext} from "@/context/AlertContext";
import {SessionContext} from "@/context/SessionContext";
import System from "@/lib/models/System";
import InputField from "@/components/form/InputField";
import {useTranslations} from "next-intl";
import {LangContext, LangContextProps} from "@/context/LangContext";
import OfflineContext, {OfflineContextType} from "@/context/OfflineContext";
import {BookContext} from "@/context/BookContext";
import {LocalSyncQueueContext, LocalSyncQueueContextProps} from "@/context/SyncQueueContext";
import {BooksSyncContext, BooksSyncContextProps} from "@/context/BooksSyncContext";
import {SyncedBook} from "@/lib/models/SyncedBook";
import {SeriesContext, SeriesContextProps} from "@/context/SeriesContext";
import {SeriesSyncContext, SeriesSyncContextProps} from "@/context/SeriesSyncContext";
import {SyncedSeries} from "@/lib/models/SyncedSeries";

interface WorldElementInputProps {
    sectionLabel: string;
    sectionType: string;
}

function getElementTypeNumber(sectionType: string): number {
    const typeMap: { [key: string]: number } = {
        'laws': 0,
        'biomes': 1,
        'issues': 2,
        'customs': 3,
        'kingdoms': 4,
        'climate': 5,
        'resources': 6,
        'wildlife': 7,
        'arts': 8,
        'ethnicGroups': 9,
        'socialClasses': 10,
        'importantCharacters': 11,
    };
    return typeMap[sectionType] ?? 0;
}

export default function WorldElementComponent({sectionLabel, sectionType}: WorldElementInputProps) {
    const t = useTranslations();
    const {lang} = useContext<LangContextProps>(LangContext);
    const {isCurrentlyOffline} = useContext<OfflineContextType>(OfflineContext);
    const {addToQueue} = useContext<LocalSyncQueueContextProps>(LocalSyncQueueContext);
    const {localSyncedBooks} = useContext<BooksSyncContextProps>(BooksSyncContext);
    const {book} = useContext(BookContext);
    const {worlds, setWorlds, selectedWorldIndex, isSeriesMode} = useContext(WorldContext);
    const {errorMessage} = useContext(AlertContext);
    const {session} = useContext(SessionContext);
    const {seriesId, localSeries} = useContext<SeriesContextProps>(SeriesContext);
    const {localSyncedSeries} = useContext<SeriesSyncContextProps>(SeriesSyncContext);
    
    const [newElementName, setNewElementName] = useState<string>('');
    
    async function handleRemoveElement(
        section: keyof WorldProps,
        index: number,
    ): Promise<void> {
        try {
            let response: boolean;
            const elementId = (worlds[selectedWorldIndex][section] as WorldElement[])[index].id;
            if (isSeriesMode) {
                const deleteData = {elementId: elementId};
                if (isCurrentlyOffline() || localSeries) {
                    response = await window.electron.invoke<boolean>('db:series:world:element:delete', deleteData);
                } else {
                    response = await System.authDeleteToServer<boolean>('series/world/element/delete', deleteData, session.accessToken, lang);
                    if (localSyncedSeries.find((s: SyncedSeries): boolean => s.id === seriesId)) {
                        addToQueue('db:series:world:element:delete', deleteData);
                    }
                }
            } else if (isCurrentlyOffline() || book?.localBook) {
                response = await window.electron.invoke<boolean>('db:book:world:element:remove', {
                    elementId: elementId,
                });
            } else {
                response = await System.authDeleteToServer<boolean>('book/world/element/delete', {
                    elementId: elementId,
                }, session.accessToken, lang);

                if (localSyncedBooks.find((syncedBook: SyncedBook): boolean => syncedBook.id === book?.bookId)) {
                    addToQueue('db:book:world:element:remove', {
                        elementId: elementId,
                    });
                }
            }
            if (!response) {
                errorMessage(t("worldSetting.unknownError"))
            }
            const updatedWorlds: WorldProps[] = [...worlds];
            (updatedWorlds[selectedWorldIndex][section] as WorldElement[]).splice(
                index,
                1,
            );
            setWorlds(updatedWorlds);
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.toString());
            } else {
                errorMessage(t("worldElementComponent.errorUnknown"));
            }
        }
    }
    
    async function handleAddElement(section: keyof WorldProps): Promise<void> {
        if (newElementName.trim() === '') {
            errorMessage(t("worldElementComponent.emptyField", {section: sectionLabel}));
            return;
        }
        try {
            let elementId: string;
            if (isSeriesMode) {
                const addData = {
                    worldId: worlds[selectedWorldIndex].id,
                    elementType: getElementTypeNumber(section as string),
                    name: newElementName,
                };
                if (isCurrentlyOffline() || localSeries) {
                    elementId = await window.electron.invoke<string>('db:series:world:element:add', addData);
                } else {
                    elementId = await System.authPostToServer<string>(
                        'series/world/element/add',
                        addData,
                        session.accessToken,
                        lang
                    );
                    if (localSyncedSeries.find((s: SyncedSeries): boolean => s.id === seriesId)) {
                        addToQueue('db:series:world:element:add', addData);
                    }
                }
                if (!elementId) {
                    errorMessage(t("worldSetting.unknownError"))
                    return;
                }
            } else if (isCurrentlyOffline() || book?.localBook) {
                elementId = await window.electron.invoke<string>('db:book:world:element:add', {
                    elementType: section,
                    worldId: worlds[selectedWorldIndex].id,
                    elementName: newElementName,
                });
            } else {
                elementId = await System.authPostToServer('book/world/element/add', {
                    elementType: section,
                    worldId: worlds[selectedWorldIndex].id,
                    elementName: newElementName,
                }, session.accessToken, lang);

                if (localSyncedBooks.find((syncedBook: SyncedBook): boolean => syncedBook.id === book?.bookId)) {
                    addToQueue('db:book:world:element:add', {
                        elementType: section,
                        worldId: worlds[selectedWorldIndex].id,
                        elementId,
                        elementName: newElementName,
                    });
                }
            }
            if (!elementId) {
                errorMessage(t("worldSetting.unknownError"))
                return;
            }
            const updatedWorlds: WorldProps[] = [...worlds];
            (updatedWorlds[selectedWorldIndex][section] as WorldElement[]).push({
                id: elementId,
                name: newElementName,
                description: '',
            });
            setWorlds(updatedWorlds);
            setNewElementName('');
        } catch (e: unknown) {
            if (e instanceof Error) {
                errorMessage(e.message);
            } else {
                errorMessage(t("worldElementComponent.errorUnknown"));
            }
        }
    }
    
    function handleElementChange(
        section: keyof WorldProps,
        index: number,
        field: keyof WorldElement,
        value: string,
    ): void {
        const updatedWorlds: WorldProps[] = [...worlds];
        const sectionElements = updatedWorlds[selectedWorldIndex][
            section
            ] as WorldElement[];
        sectionElements[index] = {...sectionElements[index], [field]: value};
        setWorlds(updatedWorlds);
    }
    
    return (
        <div className="space-y-4">
            {Array.isArray(worlds[selectedWorldIndex][sectionType as keyof WorldProps]) &&
                (worlds[selectedWorldIndex][sectionType as keyof WorldProps] as WorldElement[]).map(
                    (element: WorldElement, index: number) => (
                        <div key={element.id}
                             className="bg-secondary/30 rounded-xl p-4 border-l-4 border-primary shadow-sm hover:shadow-md transition-all duration-200">
                            <div className="mb-2">
                                <InputField input={<TextInput
                                    value={element.name}
                                    setValue={(e: ChangeEvent<HTMLInputElement>) => handleElementChange(sectionType as keyof WorldProps, index, 'name', e.target.value)}
                                    placeholder={t("worldElementComponent.namePlaceholder", {section: sectionLabel.toLowerCase()})}
                                />}
                                            removeButtonCallBack={(): Promise<void> => handleRemoveElement(sectionType as keyof WorldProps, index)}/>
                            </div>
                            <TexteAreaInput
                                value={element.description}
                                setValue={(e) => handleElementChange(sectionType as keyof WorldProps, index, 'description', e.target.value)}
                                placeholder={t("worldElementComponent.descriptionPlaceholder", {section: sectionLabel.toLowerCase()})}
                            />
                        </div>
                    )
                )
            }
            <InputField input={<TextInput
                value={newElementName}
                setValue={(e: ChangeEvent<HTMLInputElement>): void => setNewElementName(e.target.value)}
                placeholder={t("worldElementComponent.newPlaceholder", {section: sectionLabel.toLowerCase()})}
            />} addButtonCallBack={(): Promise<void> => handleAddElement(sectionType as keyof WorldProps)}/>
        </div>
    );
}