import * as THREE from 'three';
import type { BodyTextureLayers } from './body-composition';

export function applyBodyTextureLayers(mesh: THREE.SkinnedMesh, material: THREE.MeshStandardMaterial,
  layers: BodyTextureLayers, baseMap: THREE.Texture, decalMap: THREE.Texture) {
  const uv = (index: number) => mesh.geometry.getAttribute(index === 0 ? 'uv' : `uv${index}`);
  const baseUv = uv(layers.baseUvSetIndex), decalUv = uv(layers.decalUvSetIndex);
  const count = mesh.geometry.getAttribute('position').count;
  if (!baseUv || !decalUv || baseUv.count !== count || decalUv.count !== count) {
    throw new Error(`Faltan los UV originales de ${mesh.userData.gfSourceName}: ${layers.sourceMaterial}`);
  }
  // Alias the preserved GLB channels. No coordinates or vertex data are changed.
  mesh.geometry.setAttribute('gfBodyBaseUv', baseUv);
  mesh.geometry.setAttribute('gfBodyDecalUv', decalUv);
  material.map = baseMap;
  if (layers.sourceAlphaProperty) {
    material.alphaTest = layers.sourceAlphaProperty.alphaTestEnabled ? layers.sourceAlphaProperty.alphaCutoff : 0;
    material.transparent = Boolean(layers.sourceAlphaProperty.flags & 1);
  }
  material.onBeforeCompile = shader => {
    shader.uniforms.gfBodyDecalMap = { value: decalMap };
    shader.vertexShader = shader.vertexShader.replace('#include <uv_pars_vertex>', `#include <uv_pars_vertex>
attribute vec2 gfBodyBaseUv;
attribute vec2 gfBodyDecalUv;
varying vec2 vGfBodyBaseUv;
varying vec2 vGfBodyDecalUv;`)
      .replace('#include <uv_vertex>', `#include <uv_vertex>
vGfBodyBaseUv = gfBodyBaseUv;
vGfBodyDecalUv = gfBodyDecalUv;`);
    shader.fragmentShader = shader.fragmentShader.replace('#include <map_pars_fragment>', `#include <map_pars_fragment>
uniform sampler2D gfBodyDecalMap;
varying vec2 vGfBodyBaseUv;
varying vec2 vGfBodyDecalUv;`)
      .replace('#include <map_fragment>', `
vec4 gfBase = texture2D(map, vGfBodyBaseUv);
vec4 gfDecal = texture2D(gfBodyDecalMap, vGfBodyDecalUv);
float gfAlpha = gfDecal.a + gfBase.a * (1.0 - gfDecal.a);
vec3 gfRgb = (gfDecal.rgb * gfDecal.a + gfBase.rgb * gfBase.a * (1.0 - gfDecal.a)) / max(gfAlpha, 0.000001);
diffuseColor *= vec4(gfRgb, gfAlpha);`);
  };
  material.customProgramCacheKey = () => 'origin-body-source-two-uv-base-decal-v1';
  material.userData.gfBodyTextureLayers = { ...layers, status: 'SOURCE_TWO_UV_BASE_DECAL', uvAttributesPresent: true };
}
