import set from "lodash/set"
import has from "lodash/has"
import unset from "lodash/unset"
import get from "lodash/get"

export type FileTree = {
  [key: string]: FileTree | string
}

export const createFile = (
  path: string,
  fileTree: FileTree,
  value: string,
): FileTree => {
  if (existsFile(path, fileTree)) {
    throw new Error(`File ${path} already exists`)
  }
  const pathError = validatePath(path, fileTree)
  if (pathError) {
    throw new Error(pathError)
  }

  return set(fileTree, path.split("/"), value)
}

export const validatePath = (
  path: string,
  fileTree: FileTree,
): string | undefined => {
  const paths = path.split("/")
  paths.pop() // The last item is the filename
  for (let i = 0; i <= paths.length; i++) {
    const path = paths.slice(0, i + 1)
    const pathStr = path.join("/")
    if (existsFile(pathStr, fileTree) && !isFolder(pathStr, fileTree)) {
      return `Invalid path. The path ${path} is not a folder`
    }
  }
}

export const updateFile = (
  path: string,
  content: FileTree | string,
  fileTree: FileTree,
): FileTree => {
  return set(fileTree, path.split("/"), content)
}

export const existsFile = (path: string, fileTree: FileTree) => {
  return has(fileTree, path.split("/"))
}

export const removeFile = (path: string, fileTree: FileTree) => {
  const updatedFileTree = { ...fileTree }
  unset(updatedFileTree, path.split("/"))
  return updatedFileTree
}

export const moveFile = (
  currentPath: string,
  newPath: string,
  fileTree: FileTree,
) => {
  const content = getFileContent(currentPath, fileTree)
  if (typeof content !== "string") {
    throw new Error("Move folders is not allowed")
  }
  fileTree = removeFile(currentPath, fileTree)
  fileTree = createFile(newPath, fileTree, content)
  return fileTree
}

export const getFileContent = (path: string, fileTree: FileTree) => {
  return get(fileTree, path.split("/")) as string | FileTree
}

export const isFolder = (path: string, fileTree: FileTree) => {
  const content = getFileContent(path, fileTree)
  return typeof content === "object"
}

export const traverse = (
  fileTree: FileTree,
  callback: (
    content: FileTree | string,
    filename: string,
    fullPath: string,
  ) => void,
  parent?: string,
) => {
  Object.keys(fileTree).forEach((filename) => {
    const fullPath = parent ? `${parent}/${filename}` : filename
    const content = fileTree[filename]
    callback(content, filename, fullPath)
    if (typeof content === "object") {
      traverse(content, callback, fullPath)
    }
  })
}
