import * as THREE from 'three';
import { Water } from 'three/addons/objects/Water.js';
import { EnvConfig } from './config.js';
import { loadGlb } from './loadAssets.js';
import { TRACK_DATA } from './trackData.js';

export function createWater(
  scene: THREE.Scene,
  sunDirection: THREE.Vector3,
  waterNormals: THREE.Texture
): Water {
  const geometry = new THREE.PlaneGeometry(EnvConfig.water.size, EnvConfig.water.size);
  const water = new Water(geometry, {
    textureWidth: 512,
    textureHeight: 512,
    waterNormals,
    sunDirection: sunDirection.clone().normalize(),
    sunColor: 0xffffff,
    waterColor: EnvConfig.water.color,
    distortionScale: EnvConfig.water.distortionScale,
    fog: false,
    side: THREE.DoubleSide,
  });
  water.rotation.x = -Math.PI / 2;
  water.position.y = EnvConfig.water.positionY;
  scene.add(water);
  return water;
}

function findMesh(group: THREE.Group, name: string): THREE.Mesh | null {
  let found: THREE.Mesh | null = null;
  group.traverse((child) => {
    if (child instanceof THREE.Mesh && child.name === name) found = child;
  });
  return found;
}

export async function createTrack(scene: THREE.Scene, baseUrl: string): Promise<void> {
  const trackGlb = await loadGlb(baseUrl + 'models/track/track.glb');
  const startLineGlb = await loadGlb(baseUrl + 'models/track/starting_line.glb');

  const size = TRACK_DATA.size;
  const data = TRACK_DATA.data;

  for (let i = 0; i < data.length; i++) {
    for (let j = 0; j < data[i].length; j++) {
      const tag = data[i][j].t;
      const rotation = (data[i][j] as { r?: number }).r ?? 0;
      const gi = i * 15;
      const gj = j * 15;
      const cx = gi * size.x;
      const cy = size.y;
      const cz = gj * size.z;
      const rotY = (Math.PI / 180) * rotation;

      if (tag === 1) {
        // Start
        const start = findMesh(trackGlb, 'Object_18')?.clone();
        if (start) {
          start.scale.set(size.x, size.y, size.z);
          start.position.set(cx, cy, cz);
          start.rotation.y = rotY;
          scene.add(start);
        }
        startLineGlb.scale.set(size.x / 30, size.y / 30, size.z / 30);
        startLineGlb.position.set(cx, cy + 5, cz);
        startLineGlb.rotation.y = rotY;
        scene.add(startLineGlb.clone());
      } else if (tag === 2) {
        // Straight
        const straight = findMesh(trackGlb, 'Object_14')?.clone();
        if (straight) {
          straight.scale.set(size.x, size.y, size.z);
          straight.position.set(cx, cy, cz);
          straight.rotation.y = rotY;
          scene.add(straight);
        }
      } else if (tag === 3) {
        // Turn 90
        const turn90 = findMesh(trackGlb, 'Object_12')?.clone();
        if (turn90) {
          turn90.scale.set(size.x, size.y, size.z);
          const off = 7.685 * 2;
          let px = cx;
          let pz = cz;
          if (rotation === 0) {
            px = cx + off;
            pz = cz - off;
          } else if (rotation === 90) {
            px = cx - off;
            pz = cz - off;
          } else if (rotation === 180) {
            px = cx - off;
            pz = cz + off;
          } else if (rotation === 270) {
            px = cx + off;
            pz = cz + off;
          }
          turn90.position.set(px, cy, pz);
          turn90.rotation.y = rotY;
          scene.add(turn90);
        }
      } else if (tag === 4) {
        // Finish
        const finish = findMesh(trackGlb, 'Object_18')?.clone();
        if (finish) {
          finish.scale.set(size.x, size.y, size.z);
          finish.position.set(cx, cy, cz);
          finish.rotation.y = rotY;
          scene.add(finish);
        }
      }
    }
  }

  // Add boundaries for straight segments (Object_8)
  const completeBoundary = findMesh(trackGlb, 'Object_8');
  if (completeBoundary) {
    const halfWidth = (15 * size.x) / 2;
    for (let i = 0; i < data.length; i++) {
      for (let j = 0; j < data[i].length; j++) {
        const tag = data[i][j].t;
        const rotation = (data[i][j] as { r?: number }).r ?? 0;
        if (tag === 1 || tag === 2 || tag === 4) {
          const gi = i * 15;
          const gj = j * 15;
          const cx = gi * size.x;
          const cz = gj * size.z;
          let dx = 0, dz = 0;
          if (rotation === 0) dx = halfWidth;
          else if (rotation === 90) dz = halfWidth;
          else if (rotation === 180) dx = -halfWidth;
          else if (rotation === 270) dz = -halfWidth;

          const left = completeBoundary.clone();
          const right = completeBoundary.clone();
          left.scale.set(size.x, size.y, size.z);
          right.scale.set(size.x, size.y, size.z);
          left.position.set(cx - dx, size.y, cz - dz);
          right.position.set(cx + dx, size.y, cz + dz);
          left.rotation.y = (Math.PI / 180) * rotation;
          right.rotation.y = (Math.PI / 180) * rotation;
          scene.add(left);
          scene.add(right);
        }
      }
    }
  }
}
