///|
let new_project_name_placeholder = "__WARREN_PROJECT_NAME__"

///|
let rabbita_version_placeholder = "__RABBITA_VERSION__"

///|
let rabbita_version = "0.13.1"

///|
let default_new_project_template = "default"

///|
enum TemplateEntry {
  Dir(String, Array[TemplateEntry])
  File(String, String)
}

///|
let default_template_dir : TemplateEntry = Dir("", [
  File("moon.mod", templates.default.moon_mod),
  File("README.md", templates.default.readme_md),
  Dir("main", [
    File("moon.pkg", templates.default.main.moon_pkg),
    File("main.mbt", templates.default.main.main_mbt),
  ]),
  Dir("public", [
    File("styles.css", templates.default.public.styles_css),
    File("index.html", templates.default.public.index_html),
  ]),
])

///|
let minimized_template_dir : TemplateEntry = Dir("", [
  File("moon.mod", templates.minimized.moon_mod),
  File("moon.pkg", templates.minimized.moon_pkg),
  File("main.mbt", templates.minimized.main_mbt),
])

///|
fn render_new_project_file(content : String, project_name : String) -> String {
  let content = content.split(new_project_name_placeholder).join(project_name)
  let content = content.split(rabbita_version_placeholder).join(rabbita_version)
  content
}

///|
fn is_valid_project_name(name : String) -> Bool {
  let mut valid = name != "" && name != "." && name != ".."
  let invalid_parts = [" ", "\t", "\n", "\r", "/", "\\", ":", "\"", "'"]
  for part in invalid_parts {
    if name.contains(part) {
      valid = false
    }
  }
  valid
}

///|
async fn write_new_project_file(
  root : String,
  relative_path : String,
  content : String,
) -> Unit {
  let path = Path::join(root, relative_path).to_string()
  let parent = Path::dirname(path).to_string()
  if parent != "" && parent != "." && !@fs.exists(parent) {
    @fs.mkdir(parent, permission=0o755, recursive=true)
  }
  @fs.write_file(path, content, create_mode=CreateOrTruncate, permission=0o644)
}

///|
async fn write_template_entry(
  root : String,
  parent : String,
  entry : TemplateEntry,
  project_name : String,
) -> Unit {
  match entry {
    File(name, content) => {
      let path = if parent == "" {
        name
      } else {
        Path::join(parent, name).to_string()
      }
      write_new_project_file(
        root,
        path,
        render_new_project_file(content, project_name),
      )
    }
    Dir(name, children) => {
      let dir = if name == "" {
        parent
      } else if parent == "" {
        name
      } else {
        Path::join(parent, name).to_string()
      }
      for child in children {
        write_template_entry(root, dir, child, project_name)
      }
    }
  }
}

///|
async fn new_project(dir : String, template_name : String) -> Unit {
  let target_dir = Path::resolve(dir).to_string()
  let project_name = Path::basename(target_dir).to_owned()
  let template_dir = match template_name {
    "default" => default_template_dir
    "minimized" => minimized_template_dir
    _ => {
      println(
        "Unknown template `\{template_name}`. Available templates: default, minimized.",
      )
      return
    }
  }
  guard is_valid_project_name(project_name) else {
    abort(
      "Invalid project directory name `\{project_name}`. Avoid spaces, path separators, and quotes.",
    )
  }
  guard !@fs.exists(target_dir) else {
    abort("Target path already exists: \{target_dir}")
  }

  @fs.mkdir(target_dir, permission=0o755, recursive=true)
  write_template_entry(target_dir, "", template_dir, project_name)

  println("Created Warren project in \{target_dir}")
  println("")
  println("Next steps:")
  println("  cd \{target_dir}")
  println("  warren dev")
}

///|
test "render_new_project_file replaces placeholders" {
  inspect(
    render_new_project_file("local/__WARREN_PROJECT_NAME__", "demo"),
    content="local/demo",
  )
}

///|
test "is_valid_project_name rejects empty and path-like names" {
  inspect(is_valid_project_name("demo-app"), content="true")
  inspect(is_valid_project_name(""), content="false")
  inspect(is_valid_project_name("two words"), content="false")
  inspect(is_valid_project_name("nested/path"), content="false")
}