import chalk from "chalk"
import fs from "fs"
import Listr from "listr"
import ncp from "ncp"
import path from "path"
import { fileURLToPath } from "url"
import { promisify } from "util"

const access = promisify(fs.access)
const rename = promisify(fs.rename)
const copy = promisify(ncp)
const execSync = promisify(require("child_process").exec)

async function copyTemplateFiles(options) {
  return copy(options.templateDirectory, options.targetDirectory, {
    clobber: false,
  })
}

async function copyActionsFiles(options) {
  return copy(options.actionsDirectory, path.join(options.targetDirectory, ".github"), {
    clobber: false,
  })
}

async function createDeployAutomation(options) {
  return copy(options.sharedDirectory, options.targetDirectory, {
    clobber: false,
  })
}

async function initGit(options) {

  const createRepo = await execSync("git init", { encoding: "utf8", cwd: options.targetDirectory })

  if (createRepo.failed) {
    throw new Error("Failed to initialize git")
  }

  const renameBranch = await execSync("git checkout -b main", { encoding: "utf8", cwd: options.targetDirectory })

  if (renameBranch.failed) {
    throw new Error("Failed to create main branch")
  }
}

export async function createProject(options) {
  options = {
    ...options,
    targetDirectory: options.targetDirectory || process.cwd(),
  }

  // this normalises path in windows and unix environments
  const currentFilePath = fileURLToPath(import.meta.url)

  const templateDir = path.resolve(
    currentFilePath,
    "..",
    "..",
    "templates",
    options.template.toLowerCase(),
    options.version
  )
  const actionsDir = path.join(path.resolve(currentFilePath, "..", ".."), ".github")
  const sharedDir = path.resolve(currentFilePath, "..", "..", "shared")
  options.templateDirectory = templateDir
  options.actionsDirectory = actionsDir
  options.sharedDirectory = sharedDir

  try {
    await access(templateDir, fs.constants.R_OK)
  } catch (err) {
    console.log(err)
    console.error("%s Invalid template name", chalk.red.bold("ERROR"))
    process.exit(1)
  }

  try {
    await access(actionsDir, fs.constants.R_OK)
  } catch (err) {
    console.log(err)
    console.error("%s unable to load .github actions dir", chalk.red.bold("ERROR"))
    process.exit(1)
  }

  const tasks = new Listr([
    {
      title: "Copy project files",
      task: async () => await copyTemplateFiles(options),
    },
    {
      title: "Copy Github Actions files",
      task: async () => await copyActionsFiles(options),
    },
    {
      title: "Create build and deploy automation",
      task: async () => await createDeployAutomation(options),
    },
    {
      title: "Configure git, npm, prettier, package.json",
      task: async () => {
        await rename(`${options.targetDirectory}/template.gitignore`, `${options.targetDirectory}/.gitignore`)
        await rename(`${options.targetDirectory}/package.file`, `${options.targetDirectory}/package.json`)
        await rename(`${options.targetDirectory}/template.npmrc`, `${options.targetDirectory}/.npmrc`)
        await rename(`${options.targetDirectory}/template.prettierrc`, `${options.targetDirectory}/.prettierrc`)
      },
    },
    {
      title: "Initialize git",
      task: async () => await initGit(options),
      enabled: () => options.git,
    },
    {
      title: "Install dependencies",
      task: async () => await execSync(`npm i --legacy-peer-deps`, { encoding: "utf8", cwd: options.targetDirectory }),
      skip: () => (!options.runInstall ? "Pass --install to automatically install dependencies" : undefined),
    },
  ])

  await tasks.run()
  console.log("%s Project ready", chalk.green.bold("DONE"))
  return true
}
