import { useMemo } from "react";
import { Shape, Vector2, DoubleSide, TextureLoader, RepeatWrapping, SRGBColorSpace, NoColorSpace, } from "three";
import { useLoader } from "@react-three/fiber";
import { Extrude } from "@react-three/drei";
import useModuleStore from "../../../../../store/useModuleStore";
import { useBuilderStore } from "../../../../../store/builder/useBuilderStore";
import { useToggleView } from "../../../../../store/builder/store";
import * as Constants from "../../../../../types/world/worldConstants";

import texturePath from "../../../../../assets/textures/floor/white.png";
import texturePathDark from "../../../../../assets/textures/floor/black.png";
import material1 from "../../../../../assets/textures/floor/factory wall texture.jpg";

// floor-mat1
import material2Map from "../../../../../assets/textures/floor/tex1/MI_FactoryConcreteFloor01_BaseColor.001.jpg";
import material2NormalMap from "../../../../../assets/textures/floor/tex1/MI_FactoryConcreteFloor01_Normal.001.jpg";
import material2MetalicRoughnessMap from "../../../../../assets/textures/floor/tex1/MI_FactoryConcreteFloor01_MetallicRoughness.001.jpg";

// floor-mat2
import material3Map from "../../../../../assets/textures/floor/tex2/MI_FloorMats01_baseColor.png";
import material3NormalMap from "../../../../../assets/textures/floor/tex2/MI_FloorMats01_Normal.png";
import material3MetalicRoughnessMap from "../../../../../assets/textures/floor/tex2/MI_FloorMats01_occlusionRoughnessMetallic.png";

// floor-mat3
import material4Map from "../../../../../assets/textures/floor/tex3/metal_plate_diff_1k.jpg";
import material4RoughnessMap from "../../../../../assets/textures/floor/tex3/metal_plate_rough_1k.png";
import material4MetalicMap from "../../../../../assets/textures/floor/tex3/metal_plate_metal_1k.png";
import material4NormalMap from "../../../../../assets/textures/floor/tex3/metal_plate_nor_gl_1k.png";

function FloorInstance({ floor }: { floor: Floor }) {
    const { togglView } = useToggleView();
    const { activeModule } = useModuleStore();
    const { selectedFloor, setSelectedFloor, setSelectedDecal } = useBuilderStore();
    const savedTheme = localStorage.getItem("theme");

    const materials: Record<
        string,
        {
            map: string;
            roughnessMap?: string;
            metalnessMap?: string;
            normalMap?: string;
            textureTileScale?: [number, number];
        }
    > = {
        "Default Material": { map: savedTheme === "dark" ? texturePathDark : texturePath, },
        "Material 1": { map: material1 },
        "Material 2": {
            map: material2Map,
            roughnessMap: material2MetalicRoughnessMap,
            metalnessMap: material2MetalicRoughnessMap,
            normalMap: material2NormalMap,
            textureTileScale: [0.1, 0.1],
        },
        "Material 3": {
            map: material3Map,
            roughnessMap: material3MetalicRoughnessMap,
            metalnessMap: material3MetalicRoughnessMap,
            normalMap: material3NormalMap,
            textureTileScale: [0.35, 0.5],
        },
        "Material 4": {
            map: material4Map,
            roughnessMap: material4RoughnessMap,
            metalnessMap: material4MetalicMap,
            normalMap: material4NormalMap,
        },
    };

    const shape = useMemo(() => {
        const shape = new Shape();
        const points = floor.points.map((p) => new Vector2(p.position[0], p.position[2]));
        if (points.length < 3) return null;
        shape.moveTo(points[0].x, points[0].y);
        for (let i = 1; i < points.length; i++) {
            shape.lineTo(points[i].x, points[i].y);
        }
        return shape;
    }, [floor]);

    const textureScale = Constants.floorConfig.textureScale;

    // Helper function to handle texture maps and filter out null values
    function getMaterialMaps(material: any, defaultMap: any) {
        const materialMap = material.map || defaultMap;
        const normalMap = material.normalMap || null;
        const roughnessMap = material.roughnessMap || null;
        const metalnessMap = material.metalnessMap || null;

        return [materialMap, normalMap, roughnessMap, metalnessMap].filter((texture): texture is string => texture !== null);
    }

    // Default material map
    const defaultMaterialMap = materials["Default Material"].map;

    // Get top and side material maps
    const topMaterial = materials[floor.topMaterial];
    const sideMaterial = materials[floor.sideMaterial];

    // Get the filtered lists for top and side textures
    const topTexturesList = getMaterialMaps(topMaterial, defaultMaterialMap);
    const sideTexturesList = getMaterialMaps(sideMaterial, defaultMaterialMap);

    // Use loader to load top and side textures
    const [topTexture, topNormalTexture, topRoughnessTexture, topMetalicTexture] = useLoader(TextureLoader, topTexturesList);

    const [sideTexture, sideNormalTexture, sideRoughnessTexture, sideMetalicTexture] = useLoader(TextureLoader, sideTexturesList);

    // Early exit if materials are missing
    if (!materials[floor.topMaterial] || !materials[floor.sideMaterial]) return null;

    // Combine and pair textures with their corresponding material
    const textureMaterialMap = [
        {
            textures: [
                topTexture,
                topNormalTexture,
                topRoughnessTexture,
                topMetalicTexture,
            ],
            materialKey: floor.topMaterial,
        },
        {
            textures: [
                sideTexture,
                sideNormalTexture,
                sideRoughnessTexture,
                sideMetalicTexture,
            ],
            materialKey: floor.sideMaterial,
        },
    ];

    // Apply texture settings
    textureMaterialMap.forEach(({ textures, materialKey }) => {
        const tileScale = materials[materialKey]?.textureTileScale ?? [
            textureScale,
            textureScale,
        ];

        textures.forEach((tex, idx) => {
            if (!tex) return;
            tex.wrapS = tex.wrapT = RepeatWrapping;
            tex.repeat.set(tileScale[0], tileScale[1]);
            tex.anisotropy = 16;
            // First texture is always the color map (use SRGB), others should be linear
            tex.colorSpace = idx < 1 ? SRGBColorSpace : NoColorSpace;
        });
    });

    if (!shape) return null;

    return (
        <mesh
            castShadow
            receiveShadow
            name={`Floor-${floor.floorUuid}`}
            rotation={[Math.PI / 2, 0, 0]}
            position={[0, !floor.isBeveled ? floor.floorDepth - 0.1 : floor.floorDepth - 0.2, 0,]}
            userData={floor}
            onDoubleClick={(e) => {
                if (!togglView && activeModule === "builder") {
                    if (e.object.userData.floorUuid) {
                        e.stopPropagation();
                        setSelectedFloor(e.object);
                        setSelectedDecal(null);
                    }
                }
            }}
            onPointerMissed={() => {
                if (selectedFloor && selectedFloor.userData.floorUuid === floor.floorUuid) {
                    setSelectedFloor(null);
                }
            }}
        >
            <Extrude
                name={`Floor-${floor.floorUuid}`}
                args={[shape,
                    {
                        depth: !floor.isBeveled ? floor.floorDepth : floor.floorDepth - 0.1,
                        bevelEnabled: floor.isBeveled,
                        bevelSegments: floor.bevelStrength,
                        bevelOffset: -0.1,
                        bevelSize: 0.1,
                        bevelThickness: 0.1,
                    },
                ]}
                userData={floor}
            >
                <meshPhysicalMaterial
                    attach="material-0"
                    color={Constants.floorConfig.defaultColor}
                    map={topTexture}
                    roughnessMap={topRoughnessTexture}
                    metalnessMap={topMetalicTexture}
                    normalMap={topNormalTexture}
                    roughness={1.5}
                    metalness={1.0}
                    side={DoubleSide}
                />
                <meshStandardMaterial
                    attach="material-1"
                    color={Constants.floorConfig.defaultColor}
                    map={sideTexture?.clone()}
                    roughnessMap={sideRoughnessTexture?.clone()}
                    metalnessMap={sideMetalicTexture?.clone()}
                    normalMap={sideNormalTexture?.clone()}
                    side={DoubleSide}
                />
            </Extrude>
        </mesh>
    );
}

export default FloorInstance;
