import { constants } from 'fs'
import { join } from 'node:path'
import { access, readFile, readdir } from 'fs/promises'

import type { Items, Item, Tree } from './type'

export const PIJUL_DIR = '.pijul'

export function getExtension(name: string): string | null {
  const splitted = name.split('.')
  if (splitted.length < 2) {
    return null
  } else {
    return splitted.at(-1) ?? null
  }
}

export function getParent(path: string): string {
  return path.split('/').at(-1) as string
}


export const cmpItems = (a: Item, b: Item): 0 | 1 | -1 =>
  a.depth > b.depth
    ? 1
    : a.depth < b.depth
    ? -1
    : a.isDir && !b.isDir
    ? -1
    : !a.isDir && b.isDir
    ? 1
    : ((s1: string, s2: string) => (s1 === s2 ? 0 : s1 > s2 ? 1 : -1))(
        a.name,
        b.name
      )

export const toItems = async (path = 'repo', depth = 1): Promise<Items> => {
  const tree: Items = []
  const fileCollector = async (tree: Items, path: string, depth: number) => {
    const entries = await readdir(path, { withFileTypes: true })
    return entries
      .filter((dirent) => dirent.name !== PIJUL_DIR)
      .map(async (dirent) => {
        const item = {
          isDir: !!dirent.isDirectory(),
          extension: getExtension(dirent.name),
          name: dirent.name,
          path: join(path, dirent.name),
          depth,
        }
        tree.push({ ...item, id: tree.length + 1 })
        if (dirent.isDirectory()) {
          await fileCollector(tree, join(path, dirent.name), depth + 1)
        }
      })
  }
  try {
    await Promise.all(await fileCollector(tree, path, depth))
  } catch (error) {
    console.error(error)
  } finally {
    // eslint-disable-next-line no-unsafe-finally
    return tree
  }
}

export const toTree = (data: Items, depth = 1): Tree => {
  const insertChildren = (data: Item[], depth: number) =>
    data
      .filter((item) => item.depth === depth)
      .map((item) => ({
        id: item.id,
        name: item.name,
        path: item.path,
        ...(item.isDir
          ? {
              children: insertChildren(
                data.filter((item) => item.depth > depth),
                depth + 1
              ),
            }
          : null),
      }))

  return {
    id: 0,
    name: 'root',
    children: insertChildren(data, depth),
  }
}

export async function isFile(path: string) {
  try {
    await access(path, constants.F_OK | constants.R_OK)
    return true
  } catch (error) {
    return false
  }
}

export async function getContent(path: string) {
  const isFileExist = await isFile(path)

  if (!isFileExist) {
    console.error(`The file located at ${path} do not exist`)
    return ''
  }

  try {
    const buffer = await readFile(path)
    return buffer.toString()
  } catch (error) {
    console.error(error)
    return ''
  }
}