/**
 * Model
 * Loads a 3D OBJ asset, recenters it in the scene, and applies either a texture
 * or a configurable fallback material for the editor viewport.
 */
import React, { useEffect, useLayoutEffect, useMemo } from 'react';
import { useLoader } from '@react-three/fiber';
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';
import * as THREE from 'three';
import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js';

export default function Model({ url, textureUrl, settings = {} }) {
  const obj = useLoader(OBJLoader, url);

  const PALE_TEXTURE = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
  const loadedTexture = useLoader(THREE.TextureLoader, textureUrl || PALE_TEXTURE);

  const { 
    showTexture = true, 
    opacity = 1, 
    wireframe = false,
    color = "#bbbbbb"
  } = settings;

  const displayTexture = useMemo(() => {
    if (!loadedTexture || !textureUrl || textureUrl === 'undefined') {
      return null;
    }

    const nextTexture = loadedTexture.clone();
    nextTexture.flipY = true;
    nextTexture.colorSpace = THREE.SRGBColorSpace;
    nextTexture.needsUpdate = true;
    return nextTexture;
  }, [loadedTexture, textureUrl]);

  useEffect(() => {
    return () => {
      displayTexture?.dispose();
    };
  }, [displayTexture]);

  useEffect(() => {
    if (!obj) return;

    obj.traverse((child) => {
      if (child.isMesh) {
        child.geometry.computeVertexNormals();
        child.geometry = BufferGeometryUtils.toCreasedNormals(child.geometry, THREE.MathUtils.degToRad(180));
      }
    });
  }, [obj]);

  useLayoutEffect(() => {
    if (!obj) return;

    const hasValidTexture = Boolean(displayTexture);
    
    const box = new THREE.Box3().setFromObject(obj);
    const center = new THREE.Vector3();
    box.getCenter(center);
    obj.position.set(-center.x, -center.y, -center.z);

    obj.traverse((child) => {
      if (child.isMesh) {
        if (showTexture && hasValidTexture) {
          child.material = new THREE.MeshLambertMaterial({
            map: displayTexture,
            color: '#ffffff',
            transparent: opacity < 1,
            opacity,
            side: THREE.DoubleSide,
            wireframe,
          });
        } else {
          child.material = new THREE.MeshStandardMaterial({
            color,
            roughness: 0.8,
            metalness: 0.0,
            transparent: opacity < 1,
            opacity,
            side: THREE.DoubleSide,
            wireframe,
          });
        }
        child.material.needsUpdate = true;
      }
    });
  }, [obj, displayTexture, showTexture, wireframe, opacity, color]);

  return (
    <group scale={0.2}>
      <primitive object={obj} />
    </group>
  );
}