import { useMemo } from "react";
import * as Constants from "../../../../types/world/worldConstants";
import { Color, DoubleSide, ShaderMaterial, MeshStandardMaterial } from "three";
import useShaderReader from "../../../../utils/scene/useShaderReader";

import edgeVertex from "../../../../assets/shaders/box/box1.vert.glsl";
import edgeFragment from "../../../../assets/shaders/box/box1.frag.glsl";

type BoxMaterialProps = {
    color?: string;
    transparent?: boolean;
    depthWrite?: boolean;
    depthTest?: boolean;
    opacity?: number;
    polygonOffset?: boolean;
    polygonOffsetFactor?: number;
    polygonOffsetUnits?: number;
    variant?: "box1" | "none";
    isHovered?: boolean;
} & (
    | {
          variant?: "none";
      }
    | {
          variant: "box1";
          uOuterColor?: string;
          uInnerColor?: string;
          uDeleteColor?: string;
          isDeleteHovered?: boolean;
      }
);

function BoxMaterial({
    color = "white",
    transparent = false,
    depthWrite = true,
    depthTest = true,
    opacity = 1,
    polygonOffset = false,
    polygonOffsetFactor,
    polygonOffsetUnits,
    variant = "none",
    isHovered = false,
    ...rest
}: Readonly<BoxMaterialProps>) {
    const vertexShader = useShaderReader(edgeVertex);
    const fragmentShader = useShaderReader(edgeFragment);

    const box1Props = variant === "box1" ? (rest as { uOuterColor?: string; uInnerColor?: string; uDeleteColor: string; isDeleteHovered?: boolean }) : null;

    const uOuterColor = box1Props?.uOuterColor ?? Constants.pointConfig.defaultOuterColor;
    const uInnerColor = box1Props?.uInnerColor ?? Constants.pointConfig.defaultInnerColor;
    const uDeleteColor = box1Props?.uDeleteColor ?? Constants.pointConfig.deleteColor;
    const isDeleteHovered = box1Props?.isDeleteHovered ?? false;

    const finalOuterColor = isDeleteHovered ? uDeleteColor : uOuterColor;
    const finalInnerColor = isDeleteHovered ? uDeleteColor : isHovered ? uOuterColor : uInnerColor;
    const finalColor = isDeleteHovered ? uDeleteColor : color;

    const polygonOffsetProps = polygonOffset
        ? {
              polygonOffsetFactor: polygonOffsetFactor ?? -1,
              polygonOffsetUnits: polygonOffsetUnits ?? -1,
          }
        : {};

    const basicMaterial = useMemo(() => {
        if (variant !== "none") return null;
        return new MeshStandardMaterial({
            color: new Color(finalColor),
            side: DoubleSide,
            transparent,
            depthWrite,
            depthTest,
            polygonOffset,
            ...polygonOffsetProps,
            opacity,
        });
    }, [finalColor, transparent, depthWrite, depthTest, polygonOffset, polygonOffsetProps, opacity, variant]);

    const boxMaterial1 = useMemo(() => {
        if (!vertexShader || !fragmentShader || variant !== "box1") return null;

        return new ShaderMaterial({
            side: DoubleSide,
            vertexShader,
            fragmentShader,
            uniforms: {
                uOuterColor: { value: new Color(finalOuterColor) },
                uInnerColor: { value: new Color(finalInnerColor) },
            },
            transparent,
            depthWrite,
            depthTest,
            polygonOffset,
            ...polygonOffsetProps,
            opacity,
        });
    }, [finalOuterColor, finalInnerColor, vertexShader, fragmentShader, transparent, depthWrite, depthTest, polygonOffset, polygonOffsetProps, opacity, variant]);

    if (variant === "none" && basicMaterial) return <primitive object={basicMaterial} attach="material" />;
    if (variant === "box1" && boxMaterial1) return <primitive object={boxMaterial1} attach="material" />;

    return null;
}

export default BoxMaterial;
