import { execSync } from 'node:child_process'
import { readdirSync, copyFileSync, unlinkSync, mkdirSync, existsSync } from 'node:fs'
import { join, resolve } from 'node:path'
import { exit } from 'node:process'

/**
 * This script is used to transform gltf and glb files into Threlte components.
 * It uses the `@threlte/gltf` package to do so.
 * It works in two steps:
 * 1. Transform the gltf/glb files located in the sourceDir directory
 * 2. Move the Threlte components to the targetDir directory
 */
const configuration = {
  sourceDir: resolve(join('static', 'models')),
  targetDir: resolve(join('src', 'lib', 'components', 'models')),
  overwrite: false,
  root: '/models/',
  types: true,
  keepnames: false,
  meta: false,
  shadows: false,
  printwidth: 120,
  precision: 2,
  draco: null,
  preload: false,
  suspense: false,
  isolated: false,
  transform: {
    enabled: false,
    resolution: 1024,
    simplify: {
      enabled: false,
      weld: 0.0001,
      ratio: 0.75,
      error: 0.001
    }
  }
}

// if the target directory doesn't exist, create it
mkdirSync(configuration.targetDir, { recursive: true })

// throw error if source directory doesn't exist
if (!existsSync(configuration.sourceDir)) {
  throw new Error(`Source directory ${configuration.sourceDir} doesn't exist.`)
}

// read the directory, filter for .glb and .gltf files and files *not* ending
// with -transformed.gltf or -transformed.glb as these should not be transformed
// again.
const gltfFiles = readdirSync(configuration.sourceDir).filter((file) => {
  return (
    (file.endsWith('.glb') || file.endsWith('.gltf')) &&
    !file.endsWith('-transformed.gltf') &&
    !file.endsWith('-transformed.glb')
  )
})

if (gltfFiles.length === 0) {
  console.log('No gltf or glb files found.')
  exit()
}

const filteredGltfFiles = gltfFiles.filter((file) => {
  if (!configuration.overwrite) {
    const componentFilename = file.split('.').slice(0, -1).join('.') + '.svelte'
    const componentPath = join(configuration.targetDir, componentFilename)
    if (existsSync(componentPath)) {
      console.error(`File ${componentPath} already exists, skipping.`)
      return false
    }
  }
  return true
})

if (filteredGltfFiles.length === 0) {
  console.log('No gltf or glb files to process.')
  exit()
}

filteredGltfFiles.forEach((file) => {
  // run the gltf transform command on every file
  const path = join(configuration.sourceDir, file)

  // parse the configuration
  const args = []
  if (configuration.root) args.push(`--root ${configuration.root}`)
  if (configuration.types) args.push('--types')
  if (configuration.keepnames) args.push('--keepnames')
  if (configuration.meta) args.push('--meta')
  if (configuration.shadows) args.push('--shadows')
  args.push(`--printwidth ${configuration.printwidth}`)
  args.push(`--precision ${configuration.precision}`)
  if (configuration.draco) args.push(`--draco ${configuration.draco}`)
  if (configuration.preload) args.push('--preload')
  if (configuration.suspense) args.push('--suspense')
  if (configuration.isolated) args.push('--isolated')
  if (configuration.transform.enabled) {
    args.push(`--transform`)
    args.push(`--resolution ${configuration.transform.resolution}`)
    if (configuration.transform.simplify.enabled) {
      args.push(`--simplify`)
      args.push(`--weld ${configuration.transform.simplify.weld}`)
      args.push(`--ratio ${configuration.transform.simplify.ratio}`)
      args.push(`--error ${configuration.transform.simplify.error}`)
    }
  }
  const formattedArgs = args.join(' ')

  // run the command
  const cmd = `npx @threlte/gltf@latest ${path} ${formattedArgs}`
  try {
    execSync(cmd, {
      cwd: configuration.sourceDir
    })
  } catch (error) {
    console.error(`Error transforming model: ${error}`)
  }
})

// read dir again, but search for .svelte files only.
const svelteFiles = readdirSync(configuration.sourceDir).filter((file) => file.endsWith('.svelte'))

svelteFiles.forEach((file) => {
  // now move every file to /src/components/models
  const path = join(configuration.sourceDir, file)
  const newPath = join(configuration.targetDir, file)
  copyFile: try {
    // Sanity check, we checked earlier if the file exists. Still, the CLI takes
    // a while, so who knows what happens in the meantime.
    if (!configuration.overwrite) {
      // check if file already exists
      if (existsSync(newPath)) {
        console.error(`File ${newPath} already exists, skipping.`)
        break copyFile
      }
    }
    copyFileSync(path, newPath)
  } catch (error) {
    console.error(`Error copying file: ${error}`)
  }

  // remove the file from /static/models
  try {
    unlinkSync(path)
  } catch (error) {
    console.error(`Error removing file: ${error}`)
  }
})
