import { DragEvent } from 'react'
import { convertSwarmFile } from './SwarmFile'

const indexHtmls = ['index.html', 'index.htm']

export function detectIndexHtml(files: SwarmFile[]): string | false {
  if (!files.length) {
    return false
  }

  const exactMatch = files.find(x => indexHtmls.includes(x.path))

  if (exactMatch) {
    return exactMatch.name
  }

  const prefix = files[0].path.split('/')[0] + '/'

  const allStartWithSamePrefix = files.every(x => x.path.startsWith(prefix))

  if (allStartWithSamePrefix) {
    const match = files.find(x => indexHtmls.map(y => prefix + y).includes(x.path))

    if (match) {
      return match.name
    }
  }

  return false
}

export function getMetadata(files: SwarmFile[]): Metadata {
  const size = files.reduce((total, item) => total + item.size, 0)
  const isWebsite = Boolean(detectIndexHtml(files))
  const name = getAssetNameFromFiles(files)
  const type = files.length === 1 ? files[0].type : 'folder'
  const count = files.length

  return { size, name, type, isWebsite, count }
}

export function getAssetNameFromFiles(files: SwarmFile[]): string {
  if (files.length === 1) return files[0].name

  if (files.length > 0) {
    const prefix = files[0].path.split('/')[0]

    // Only if all files have a common prefix we can use it as a folder name
    if (files.every(f => f.path.split('/')[0] === prefix)) return prefix
  }

  return 'unknown'
}

/**
 * Directory Typeguard
 */
function isDirectory(entry: FileSystemEntry): entry is FileSystemDirectoryEntry {
  return entry.isDirectory
}

/**
 * File Typeguard
 */
function isFile(entry: FileSystemEntry): entry is FileSystemFileEntry {
  return entry.isFile
}

function readEntries(directoryReader: FileSystemDirectoryReader): Promise<FileSystemFileEntry[]> {
  return new Promise((resolve, reject) => {
    directoryReader.readEntries(async entries => {
      const files = []

      for (let i = 0; i < entries.length; i++) {
        const fls = await scanFiles(entries[i])
        files.push(...fls)
      }
      resolve(files)
    }, reject)
  })
}

/**
 * Promisified item.file function
 */
function fileEntry2File(item: FileSystemFileEntry): Promise<SwarmFile> {
  return new Promise((resolve, reject) => {
    item.file((file: File) => resolve(convertSwarmFile(file, item.fullPath)), reject)
  })
}

function scanFiles(item: FileSystemEntry): Promise<FileSystemFileEntry[]> {
  return new Promise(async (resolve, reject) => {
    if (isDirectory(item)) {
      const directoryReader = item.createReader()
      const files = await readEntries(directoryReader)
      resolve(files)
    } else if (isFile(item)) {
      resolve([item])
    } else reject('Not a file nor directory')
  })
}

export async function handleDrop(ev: DragEvent): Promise<SwarmFile[]> {
  const files: SwarmFile[] = []

  if (ev.dataTransfer.items) {
    const { items } = ev.dataTransfer
    const entries = []

    for (let i = 0; i < items.length; i++) {
      const item = items[i]
      const entry = item.webkitGetAsEntry()

      if (entry) entries.push(...(await scanFiles(entry)))
    }

    // This is deliberately a separate loop because item.file() function mutates the dataTransferItems object
    for (let i = 0; i < entries.length; i++) {
      const f = await fileEntry2File(entries[i])
      files.push(f)
    }
  } else {
    // Use DataTransfer interface to access the file(s), this is a fallback as we can not handle directories here (even though this API is newer)
    for (let i = 0; i < ev.dataTransfer.files.length; i++) files.push(convertSwarmFile(ev.dataTransfer.files[i]))
  }

  return files
}