// pkg.v — `vr init` / `vr get` / `vr install`: a minimal package manager.
//
// Packages are fetched into ./vendor/<name>/ and recorded in vr.mod as
// `dep "<spec>"`. The compiler resolves `import "pkg/file"` against vendor/
// automatically (see compiler/compiler.v). Supported package specs:
//
//   vr get owner/repo         clone from GitHub (https://github.com/owner/repo)
//   vr get https://host/repo  clone any git repository
//   vr get ./local/path       copy a local directory
module main

import os

const manifest_file = 'vr.mod'
const vendor_dir = 'vendor'

fn toolchain_init(args []string) ! {
	pkg := if args.len > 0 { args[0] } else { os.base(os.getwd()) }
	if os.exists(manifest_file) {
		return error('${manifest_file} already exists here')
	}
	os.write_file(manifest_file, 'module ${pkg}\nversion 0.1.0\n\n# deps:\n# dep "owner/repo"\n')!
	os.mkdir('vendor') or {}
	if !os.exists('main.vr') {
		os.write_file('main.vr', 'fn main() {\n\tprintln("hello from ${pkg}")\n}\n')!
	}
	if !os.exists('build.vrmm') {
		os.write_file('build.vrmm', '// build.vrmm — build instructions for the VuurRaaf toolchain.
//
//   vr make           runs main()
//   vr make clean     runs the clean() target
//
// Build builtins: build_compile, build_assemble, build_link, build_run,
// build_test, build_bench, build_clean, build_exec, build_exec_status,
// build_exists, build_mkdir, build_rm, build_copy, build_glob, build_ls,
// build_base, build_dir, build_join, build_root.

fn main() {
	build_compile("main.vr", "main.vobj")
	build_link(["main.vobj"], "main.vbin")
	println("built main.vbin — run it with: vr run main.vbin")
}

fn clean() {
	build_clean()
}
')!
	}
	println('initialized project "${pkg}" (${manifest_file}, main.vr, build.vrmm)')
}

fn toolchain_get(args []string) ! {
	if args.len == 0 {
		return error('usage: vr get <owner/repo | git-url | ./path>')
	}
	spec := args[0]
	// determine the package name and how to fetch it
	mut pkg := ''
	if spec.starts_with('./') || spec.starts_with('/') {
		pkg = os.base(spec)
	} else if spec.contains('://') {
		pkg = os.base(spec)
	} else if spec.contains('/') {
		pkg = spec.all_after('/')
	} else {
		pkg = spec
	}
	if pkg.ends_with('.git') {
		pkg = pkg[..pkg.len - 4]
	}
	if pkg == '' || pkg == '.' {
		return error('cannot derive a package name from "${spec}"')
	}
	os.mkdir(vendor_dir) or {}
	dst := os.join_path(vendor_dir, pkg)
	if os.exists(dst) {
		return error('package "${pkg}" already exists at ${dst}')
	}
	if spec.starts_with('./') || spec.starts_with('/') {
		copy_dir(spec, dst) or { return error('cannot copy ${spec}: ${err.msg()}') }
	} else {
		url := if spec.contains('://') { spec } else { 'https://github.com/${spec}.git' }
		println('cloning ${url} -> ${dst} ...')
		r := os.exec(['git', 'clone', '--depth', '1', url, dst])
		if r.exit_code != 0 {
			return error('git clone failed: ${r.output}')
		}
	}
	// record the dependency in the manifest (idempotent)
	if os.exists(manifest_file) {
		manifest := os.read_file(manifest_file) or { '' }
		if !manifest.contains('dep "${spec}"') {
			os.write_file(manifest_file, manifest.trim_space() + '\ndep "${spec}"\n') or {
				return error('cannot update ${manifest_file}: ${err.msg()}')
			}
		}
	}
	println('added package "${pkg}" (${spec})')
}

fn toolchain_install() ! {
	if !os.exists(manifest_file) {
		return error('no ${manifest_file} found — run `vr init` first')
	}
	manifest := os.read_file(manifest_file)!
	mut n := 0
	for line in manifest.split_into_lines() {
		t := line.trim_space()
		if !t.starts_with('dep "') {
			continue
		}
		mut spec := t[5..]
		if spec.ends_with('"') {
			spec = spec[..spec.len - 1]
		}
		if spec == '' {
			continue
		}
		n++
		toolchain_get([spec]) or { eprintln('  install: ${err.msg()}') }
	}
	if n == 0 {
		println('no dependencies in ${manifest_file}')
		return
	}
	println('installed ${n} package(s) into ${vendor_dir}/')
}

// copy_dir recursively copies a local directory tree.
fn copy_dir(src string, dst string) ! {
	if !os.is_dir(src) {
		return error('${src} is not a directory')
	}
	os.mkdir_all(dst) or { return error('cannot create ${dst}: ${err.msg()}') }
	for entry in os.ls(src) or { return error('cannot read ${src}: ${err.msg()}') } {
		sp := os.join_path(src, entry)
		dp := os.join_path(dst, entry)
		if os.is_dir(sp) {
			copy_dir(sp, dp)!
		} else if os.is_file(sp) {
			os.cp(sp, dp, os.CopyParams{}) or { return error('cannot copy ${sp}: ${err.msg()}') }
		}
	}
}

fn toolchain_list() ! {
	if !os.exists(manifest_file) {
		return error('no ${manifest_file} found here')
	}
	manifest := os.read_file(manifest_file)!
	mod_name := manifest.all_before('\n').replace('module ', '').trim_space()
	println('project: ${mod_name}')
	mut found := false
	for line in manifest.split_into_lines() {
		t := line.trim_space()
		if t.starts_with('dep "') {
			found = true
			mut spec := t[5..]
			if spec.ends_with('"') {
				spec = spec[..spec.len - 1]
			}
			mut pkg := os.base(spec)
			if pkg.ends_with('.git') {
				pkg = pkg[..pkg.len - 4]
			}
			status := if os.exists(os.join_path(vendor_dir, pkg)) { 'installed' } else { 'missing' }
			println('  ${pkg}  (${status})  <- ${spec}')
		}
	}
	if !found {
		println('  (no dependencies)')
	}
}
