import {
  bold,
  red,
} from 'colorette'
import {
  type AsyncZippable,
  zip,
} from 'fflate'
import { filesize } from 'filesize'
import {
  readdir,
  readFile,
  writeFile,
} from 'node:fs/promises'
import { promisify } from 'node:util'
import { type Plugin } from 'vite'
import path, { relative } from 'node:path'
import { exec } from 'node:child_process'

async function* traversalDir(path: string): AsyncGenerator<[ string, string, Uint8Array ]> {
  yield [ 'startDir', path, new Uint8Array(1) ]
  let dirents = await readdir(path, {withFileTypes: true})
  for (let dirent of dirents) {
    let subDir = `${path}/${dirent.name}`
    if (dirent.isDirectory()) {
      yield* traversalDir(subDir)
    } else {
      yield  [ 'file', subDir, await readFile(subDir) ]
    }
  }
  yield   [ 'endDir', path, new Uint8Array(1) ]
}

async function doZip(path: string) {
  let data: AsyncZippable = {}
  for await (const e of traversalDir(path)) {
    if (e[0] === 'file') {
      data[relative(path, e[1])] = e[2]
    }
  }
  return await promisify(zip)(data)
}

function openFolderInFileManager(folderPath: string) {
  return new Promise((resolve, reject) => {
    // 解析为绝对路径
    const absolutePath = path.resolve(folderPath)
    console.log('打开文件夹', absolutePath)
    // 根据操作系统选择不同的命令
    let command
    switch (process.platform) {
      case 'win32':
        // Windows 系统
        command = `explorer.exe "${absolutePath}"`
        break
      case 'darwin':
        // macOS 系统
        command = `open "${absolutePath}"`
        break
      case 'linux':
        // Linux 系统（使用 xdg-open）
        command = `xdg-open "${absolutePath}"`
        break
      default:
        reject(new Error(`不支持的操作系统: ${process.platform}`))
        return
    }
    exec(command, () => {
      resolve(1)
    })
  })
}

export default function zipDist(name?: string, addVersion: boolean = false): Plugin {
  if (!!name && !name.endsWith('.zip')) {
    name = `${name}.zip`
  } else if (!name) {
    let {npm_package_name = 'bundle', npm_package_version} = process.env
    name = !!npm_package_version && addVersion ?
      `${npm_package_name}-${npm_package_version}.zip` : `${npm_package_name}.zip`
  }

  return {
    name: 'vite-plugin-dist-zip',
    writeBundle: {
      sequential: true,
      order: 'post',
      handler(options, _) {
        doZip(options.dir!)
          .then(value => Promise.all([ writeFile(`./dist/${name}`, value), Promise.resolve(value.byteLength) ]))
          .then(value => console.log(red(`\n压缩完成 ${bold(`dist/${name}`)}    ${filesize(value[1])}`)))
          .then(() => openFolderInFileManager(options.dir!))
      },
    },
  }
}

