import {
  lambdaBaseURL,
  stlToGeometry,
  createHealthyResponse,
  createUnhealthyResponse,
  timeoutErrorMessage,
  RenderArgs,
  splitGziped,
} from '../common'
import { openScadToCadhubParams } from './openScadParams'
import type { XYZ, Camera } from 'src/helpers/hooks/useIdeState'

export const render = async ({ code, settings }: RenderArgs) => {
  const pixelRatio = window.devicePixelRatio || 1
  const size = {
    x: Math.round(settings.viewerSize?.width * pixelRatio),
    y: Math.round(settings.viewerSize?.height * pixelRatio),
  }
  const round1dec = (number) => Math.round((number + Number.EPSILON) * 10) / 10
  const body = JSON.stringify({
    settings: {
      size,
      viewAll: settings.viewAll,
      parameters: settings.parameters,
      camera: {
        // rounding to give our caching a chance to sometimes work
        ...settings.camera,
        dist: round1dec(settings.camera.dist),
        position: {
          x: round1dec(settings.camera.position.x),
          y: round1dec(settings.camera.position.y),
          z: round1dec(settings.camera.position.z),
        },
        rotation: {
          x: round1dec(settings.camera.rotation.x),
          y: round1dec(settings.camera.rotation.y),
          z: round1dec(settings.camera.rotation.z),
        },
      },
    },
    file: code,
  })
  if (!settings.camera.position) {
    return
  }
  try {
    const response = await fetch(lambdaBaseURL + '/openscad/preview', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body,
    })
    if (response.status === 400) {
      const { error } = await response.json()
      const cleanedErrorMessage = cleanError(error)
      return createUnhealthyResponse(new Date(), cleanedErrorMessage)
    }
    if (response.status === 502) {
      return createUnhealthyResponse(new Date(), timeoutErrorMessage)
    }
    const blob = await response.blob()
    const text = await new Response(blob).text()
    const { consoleMessage, customizerParams, type, cameraInfo } =
      splitGziped(text)
    const vecArray2Obj = (arr: number[]): XYZ => ({
      x: arr[0],
      y: arr[1],
      z: arr[2],
    })
    const camera: Camera = cameraInfo
      ? {
          dist: cameraInfo?.distance,
          position: vecArray2Obj(cameraInfo?.translation),
          rotation: vecArray2Obj(cameraInfo?.rotation),
          isScadUpdate: true,
        }
      : undefined
    return createHealthyResponse({
      type: type !== 'stl' ? 'png' : 'geometry',
      data:
        type !== 'stl'
          ? blob
          : await stlToGeometry(window.URL.createObjectURL(blob)),
      consoleMessage,
      camera,
      date: new Date(),
      customizerParams: openScadToCadhubParams(customizerParams || []),
    })
  } catch (e) {
    return createUnhealthyResponse(new Date())
  }
}

export const stl = async ({ code /*settings*/ }: RenderArgs) => {
  const body = JSON.stringify({
    settings: {},
    file: code,
  })
  try {
    const response = await fetch(lambdaBaseURL + '/openscad/stl', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body,
    })
    if (response.status === 400) {
      const { error } = await response.json()
      const cleanedErrorMessage = cleanError(error)
      return createUnhealthyResponse(new Date(), cleanedErrorMessage)
    }
    if (response.status === 502) {
      return createUnhealthyResponse(new Date(), timeoutErrorMessage)
    }
    const blob = await response.blob()
    const text = await new Response(blob).text()
    const { consoleMessage, customizerParams, type } = splitGziped(text)
    return createHealthyResponse({
      type: type !== 'stl' ? 'png' : 'geometry',
      data:
        type !== 'stl'
          ? blob
          : await stlToGeometry(window.URL.createObjectURL(blob)),
      consoleMessage,
      date: new Date(),
      customizerParams: openScadToCadhubParams(customizerParams || []),
    })
  } catch (e) {
    return createUnhealthyResponse(new Date())
  }
}

const openscad = {
  render,
  stl,
}

export default openscad

function cleanError(error) {
  return error.replace(/["|']\/tmp\/.+\/main.scad["|']/g, "'main.scad'")
}
