import * as THREE from 'three';
import { Decal } from '@react-three/drei'
import { useToggleView, useToolMode } from '../../../../store/builder/store';
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
import { retrieveImage, storeImage } from '../../../../utils/indexDB/idbUtils';

import defaultMaterial from '../../../../assets/image/fallback/fallback decal 1.png';
import useModuleStore from '../../../../store/ui/useModuleStore';
import { useEffect, useRef, useState } from 'react';

import { useDecalEventHandlers } from '../eventHandler/useDecalEventHandlers';

function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalPosition[2] }: Readonly<{ parent: Wall | Floor; visible?: boolean, decal: Decal, zPosition?: number }>) {
    const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
    const { selectedDecal, deletableDecal, setSelectedDecal, setDeletableDecal } = useBuilderStore();
    const { toolMode } = useToolMode();
    const { toggleView } = useToggleView();
    const { activeModule } = useModuleStore();
    const decalRef = useRef<any>(null);

    useEffect(() => {
        if (selectedDecal?.decalData.decalUuid === decal.decalUuid && !selectedDecal.decalMesh) {
            setSelectedDecal({ decalData: selectedDecal.decalData, decalMesh: decalRef.current });
        }
        // eslint-disable-next-line
    }, [selectedDecal])

    const { handlePointerMissed, handlePointerLeave, handleClick, handlePointerDown, handlePointerEnter } = useDecalEventHandlers({ parent, decal, visible });

    const [texture, setTexture] = useState<THREE.Texture | null>(null);

    const logDecalStatus = (decalId: string, status: string) => {
        // console.log(decalId, status);
    }

    const loadDefaultTexture = () => {
        const textureLoader = new THREE.TextureLoader();
        textureLoader.load(
            defaultMaterial,
            (fallbackTex) => {
                fallbackTex.name = "default-decal";
                setTexture(fallbackTex);
                logDecalStatus(decal.decalId, 'default-loaded');
            },
            undefined,
            (error) => {
                console.error("Error loading default decal texture:", error);
            }
        );
    };

    const loadDecalTexture = async (decalId: string) => {

        try {
            const cachedTexture = THREE.Cache.get(decalId);
            if (cachedTexture) {
                setTexture(cachedTexture);
                logDecalStatus(decalId, 'cache-loaded');
                return;
            }

            const indexedDBTexture = await retrieveImage(decalId);
            if (indexedDBTexture) {
                const blobUrl = URL.createObjectURL(indexedDBTexture);
                const textureLoader = new THREE.TextureLoader();
                textureLoader.load(
                    blobUrl,
                    (tex) => {
                        URL.revokeObjectURL(blobUrl);
                        tex.name = decalId;
                        THREE.Cache.add(decalId, tex);
                        setTexture(tex);
                        logDecalStatus(decalId, 'indexedDB-loaded');
                    },
                    undefined,
                    (error) => {
                        console.error(`Error loading texture from IndexedDB:`, error);
                        URL.revokeObjectURL(blobUrl);
                        loadFromBackend(decalId);
                    }
                );
                return;
            }

            loadFromBackend(decalId);
        } catch (error) {
            console.error("Error loading decal texture:", error);
            loadDefaultTexture();
        }
    };

    const loadFromBackend = (decalId: string) => {

        const textureUrl = `${url_Backend_dwinzo}/api/v1/DecalImage/${decalId}`;
        const textureLoader = new THREE.TextureLoader();

        textureLoader.load(
            textureUrl,
            async (tex) => {
                tex.name = decalId;
                THREE.Cache.add(decalId, tex);
                setTexture(tex);
                logDecalStatus(decalId, 'backend-loaded');

                try {
                    const response = await fetch(textureUrl);
                    const blob = await response.blob();
                    await storeImage(decalId, blob);
                } catch (error) {
                    console.error("Error storing texture in IndexedDB:", error);
                }
            },
            undefined,
            (error) => {
                echo.error(`Error loading texture from backend: ${decal.decalName}`);
                loadDefaultTexture();
            }
        );
    };

    useEffect(() => {
        if (decal.decalId) {
            loadDecalTexture(decal.decalId);
        } else {
            loadDefaultTexture();
        }
        // eslint-disable-next-line
    }, [decal.decalId]);

    useEffect(() => {
        if (!toggleView && activeModule === 'builder') {
            if (toolMode !== 'cursor' && selectedDecal) {
                setSelectedDecal(null);
            }
            if (toolMode !== '3D-Delete' && deletableDecal) {
                setDeletableDecal(null);
            }
        } else {
            if (selectedDecal) setSelectedDecal(null);
            if (deletableDecal) setDeletableDecal(null);
        }
        // eslint-disable-next-line
    }, [toggleView, toolMode, activeModule, selectedDecal, deletableDecal]);

    return (
        <Decal
            // debug
            visible={visible}
            ref={decalRef}
            position={[decal.decalPosition[0], decal.decalPosition[1], zPosition]}
            rotation={[0, 0, decal.decalRotation * (Math.PI / 180)]}
            scale={[(decal.decalType.type === 'Floor' || zPosition < 0) ? -decal.decalScale : decal.decalScale, decal.decalScale, 0.01]}
            userData={decal}
            onPointerDown={(e) => { if (e.button === 0) handlePointerDown(e) }}
            onClick={(e) => { handleClick(e) }}
            onPointerEnter={(e) => { handlePointerEnter(e) }}
            onPointerLeave={(e) => { handlePointerLeave(e) }}
            onPointerMissed={() => handlePointerMissed()}
        >
            <meshBasicMaterial
                map={texture}
                side={THREE.DoubleSide}
                polygonOffset
                polygonOffsetFactor={-1}
                transparent
                opacity={decal.decalOpacity}
            />
        </Decal>
    )
}

export default DecalInstance