import * as THREE from 'three';
import { useEffect, useRef, useState } from 'react';
import { retrieveGLTF, storeGLTF } from '../../../../../utils/indexDB/idbUtils';
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { useDeletableFloorItem, useSelectedAssets, useSelectedFloorItem, useToggleView, useToolMode } from '../../../../../store/builder/store';
import { AssetBoundingBox } from '../../functions/assetBoundingBox';
import useModuleStore from '../../../../../store/useModuleStore';
import { useSceneContext } from '../../../../scene/sceneContext';
import { SkeletonUtils } from 'three-stdlib';

import { getAssetFieldApi } from '../../../../../services/factoryBuilder/asset/floorAsset/getAssetField';
import { ModelAnimator } from './animator/modelAnimator';
import { useModelEventHandlers } from './eventHandlers/useEventHandlers';

function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendered: boolean, loader: GLTFLoader }) {
    const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
    const savedTheme: string = localStorage.getItem("theme") || "light";
    const { toolMode } = useToolMode();
    const { toggleView } = useToggleView();
    const { activeModule } = useModuleStore();
    const { assetStore } = useSceneContext();
    const { resetAnimation } = assetStore();
    const { setDeletableFloorItem } = useDeletableFloorItem();
    const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
    const [gltfScene, setGltfScene] = useState<GLTF["scene"] | null>(null);
    const [boundingBox, setBoundingBox] = useState<THREE.Box3 | null>(null);
    const [isSelected, setIsSelected] = useState(false);
    const groupRef = useRef<THREE.Group>(null);
    const [fieldData, setFieldData] = useState<any>();
    const { selectedAssets } = useSelectedAssets();

    useEffect(() => {
        if (!fieldData && asset.eventData) {
            getAssetFieldApi(asset.assetId).then((data) => {
                if (data.type === 'ArmBot') {
                    if (data.data) {
                        const fieldData: IK[] = data.data;
                        setFieldData(fieldData);
                    }
                } else if (data.type === 'Conveyor') {
                    if (data.data) {
                        const fieldData = data.data;
                        setFieldData(fieldData);
                    }
                } else if (data.type === 'Crane') {
                    if (data.data) {
                        const fieldData = data.data;
                        setFieldData(fieldData);
                    }
                }
            })
        }
    }, [asset.modelUuid, fieldData])

    useEffect(() => {
        setDeletableFloorItem(null);
        if (selectedFloorItem === null || selectedFloorItem.userData.modelUuid !== asset.modelUuid) {
            resetAnimation(asset.modelUuid);
        }
    }, [activeModule, toolMode, selectedFloorItem])

    useEffect(() => {
        if (selectedFloorItem && selectedFloorItem.userData.modelUuid === asset.modelUuid) {
            setSelectedFloorItem(groupRef.current);
        }
    }, [isRendered, selectedFloorItem])

    useEffect(() => {
        if (selectedAssets.length > 0) {
            if (selectedAssets.some((selectedAsset: THREE.Object3D) => selectedAsset.userData.modelUuid === asset.modelUuid)) {
                setIsSelected(true);
            } else {
                setIsSelected(false);
            }
        } else {
            setIsSelected(false);
        }
    }, [selectedAssets])

    useEffect(() => {
        if (gltfScene) {
            gltfScene.traverse((child: any) => {
                if (child.isMesh) {
                    child.castShadow = true;
                    child.receiveShadow = true;
                }
            })
        }
    }, [gltfScene]);

    useEffect(() => {
        // Calculate Bounding Box
        const calculateBoundingBox = (scene: THREE.Object3D) => {
            const box = new THREE.Box3().setFromObject(scene);
            setBoundingBox(box);
        };

        // Check Cache
        const assetId = asset.assetId;
        const cachedModel = THREE.Cache.get(assetId);
        if (cachedModel) {
            const clone: any = SkeletonUtils.clone(cachedModel.scene);
            clone.animations = cachedModel.animations || [];
            setGltfScene(clone);
            calculateBoundingBox(clone);
            return;
        }

        // Check IndexedDB
        retrieveGLTF(assetId).then((indexedDBModel) => {
            if (indexedDBModel) {
                const blobUrl = URL.createObjectURL(indexedDBModel);
                loader.load(
                    blobUrl,
                    (gltf) => {
                        URL.revokeObjectURL(blobUrl);
                        THREE.Cache.remove(blobUrl);
                        THREE.Cache.add(assetId, gltf);
                        setGltfScene(gltf.scene.clone());
                        calculateBoundingBox(gltf.scene);
                    },
                    undefined,
                    (error) => {
                        echo.error(`[IndexedDB] Error loading ${asset.modelName}:`);
                        URL.revokeObjectURL(blobUrl);
                    }
                );
                return;
            }

            // Fetch from Backend
            const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${assetId}`;
            loader.load(
                modelUrl,
                (gltf: GLTF) => {
                    fetch(modelUrl)
                        .then((response) => response.blob())
                        .then((modelBlob) => storeGLTF(assetId, modelBlob))
                        .then(() => {
                            THREE.Cache.add(assetId, gltf);
                            setGltfScene(gltf.scene.clone());
                            calculateBoundingBox(gltf.scene);
                        })
                        .catch((error) => {
                            console.error(
                                `[Backend] Error storing/loading ${asset.modelName}:`,
                                error
                            );
                        });
                },
                undefined,
                (error) => {
                    echo.error(`[Backend] Error loading ${asset.modelName}:`);
                }
            );
        }).catch((err) => {
            console.error("Failed to load model:", asset.assetId, err);
        });
    }, []);

    const { handleDblClick, handleClick, handlePointerOver, handlePointerOut, handleContextMenu } = useModelEventHandlers({ boundingBox, groupRef, asset });

    return (
        <group
            key={asset.modelUuid}
            name='Asset Model'
            ref={groupRef}
            uuid={asset.modelUuid}
            position={asset.position}
            rotation={asset.rotation}
            visible={asset.isVisible}
            userData={{ ...asset, fieldData: fieldData }}
            castShadow
            receiveShadow
            onDoubleClick={(e) => {
                if (!toggleView) {
                    e.stopPropagation();
                    handleDblClick(asset);
                }
            }}
            onClick={(e) => {
                if (!toggleView) {
                    e.stopPropagation();
                    handleClick(e, asset);
                }
            }}
            onPointerOver={(e) => {
                if (!toggleView) {
                    e.stopPropagation();
                    handlePointerOver(asset);
                }
            }}
            onPointerLeave={(e) => {
                if (!toggleView) {
                    e.stopPropagation();
                    handlePointerOut(e, asset);
                }
            }}
            onContextMenu={(e) => {
                e.stopPropagation();
                handleContextMenu(asset, e);
            }}
        >
            {gltfScene && (
                <>
                    {isRendered ? (
                        <>

                            <primitive object={gltfScene} />

                            <ModelAnimator asset={asset} gltfScene={gltfScene} />

                        </>
                    ) : (
                        <>
                            {!isSelected &&
                                <AssetBoundingBox name='Asset Fallback' boundingBox={boundingBox} color='gray' lineWidth={2.5} />
                            }
                        </>
                    )}
                    {isSelected &&
                        <AssetBoundingBox name='Asset BBox' boundingBox={boundingBox} color={savedTheme === "dark" ? "#c4abf1" : "#6f42c1"} lineWidth={2.7} />
                    }
                </>
            )}
        </group>
    );
}

export default Model;