#!/usr/bin/env python3

import sys as _sys
import os as _os
import subprocess as _subprocess
import argparse as _argparse
import yaml as _yaml
import string as _string
import random as _random


def file_read(path):
	handle = open(path, "r")
	content = handle.read()
	handle.close()
	return content
	

def file_write(path, content):
	directory = _os.path.dirname(path)
	if (not _os.path.exists(directory)):
		_os.makedirs(directory, exist_ok = True)
	else:
		pass
	handle = open(path, "w")
	handle.write(content)
	handle.close()
	return content
	

def get_password_hash(binary_file_path, conf_file_path, name):
	output = _subprocess.check_output([
		binary_file_path,
		"--config=%s" % conf_file_path,
		"hash-password",
		name
	])
	return output.decode("utf-8").split("\n")[0][8:]
	

def postprocess(binary_file_path, conf_file_path, data):
	password = "".join(map(lambda x: _random.choice(_string.ascii_lowercase), range(16)))
	if (len(data["users"]) <= 0):
		data["users"]["_dummy"] = {
			"displayname": "(Dummy)",
			"password": get_password_hash(binary_file_path, conf_file_path, password),
			"email": "dummy@nowhere.net",
			"groups": [],
		}
	else:
		pass
	return data
	

def data_read(user_file_path):
	data = _yaml.safe_load(file_read(user_file_path))
	data["users"].pop("_dummy", None)
	return data
	

def data_write(binary_file_path, conf_file_path, user_file_path, data):
	file_write(user_file_path, _yaml.dump(postprocess(binary_file_path, conf_file_path, data)))
	

def main():
	## args
	argument_parser = _argparse.ArgumentParser()
	argument_parser.add_argument(
		"command",
		type = str,
		choices = ["list", "show", "add", "change", "remove"],
		default = "list",
		help = "command to execute",
	)
	argument_parser.add_argument(
		"-b",
		"--binary-file-path",
		type = str,
		default = "/usr/bin/authelia",
		help = "path to the Authelia binary file",
	)
	argument_parser.add_argument(
		"-c",
		"--conf-file-path",
		type = str,
		default = "/etc/authelia/configuration.yml",
		help = "path to the Authelia configuration file",
	)
	argument_parser.add_argument(
		"-u",
		"--user-file-path",
		type = str,
		default = "/var/authelia/users.yml",
		help = "path to the user database file",
	)
	argument_parser.add_argument(
		"-n",
		"--login-name",
		type = str,
		default = None,
		help = "login name of the user; has to be unique",
	)
	argument_parser.add_argument(
		"-p",
		"--password",
		type = str,
		default = None,
		help = "password of the user",
	)
	argument_parser.add_argument(
		"-d",
		"--display-name",
		type = str,
		default = None,
		help = "display name of the user",
	)
	argument_parser.add_argument(
		"-e",
		"--email",
		type = str,
		default = None,
		help = "e-mail address of the user",
	)
	argument_parser.add_argument(
		"-x",
		"--deactivated",
		type = str,
		default = "no",
		choices = ["no", "yes"],
		help = "whether the user shall be deactivated",
	)
	args = argument_parser.parse_args()
	
	## exec
	data = data_read(args.user_file_path)
	if (args.command == "list"):
		names = sorted(data["users"].keys())
		for name in names:
			_sys.stdout.write("%s\n" % name)
	elif (args.command == "show"):
		if (args.login_name is None):
			raise ValueError("name required")
		else:
			if (not (args.login_name in data["users"])):
				raise ValueError("no such user")
			else:
				entry = data["users"][args.login_name]
				_sys.stdout.write("%s\n" % _yaml.dump(entry))
	elif (args.command == "add"):
		if (args.login_name is None):
			raise ValueError("name required")
		else:
			if (args.password is None):
				raise ValueError("password required")
			else:
				entry = {
					"disabled": (args.deactivated == "yes"),
					"displayname": (args.display_name or args.login_name),
					"password": get_password_hash(args.binary_file_path, args.conf_file_path, args.login_name),
					"email": args.email,
					"groups": [],
				}
				data["users"][args.login_name] = entry
				data_write(args.binary_file_path, args.conf_file_path, args.user_file_path, data)
	elif (args.command == "change"):
		if (args.login_name is None):
			raise ValueError("name required")
		else:
			entry = data["users"][args.login_name]
			if (args.deactivated is None):
				pass
			else:
				entry["disabled"] = (args.deactivated == "yes")
			if (args.password is None):
				pass
			else:
				entry["password"] = get_password_hash(args.binary_file_path, args.conf_file_path, args.login_name)
			if (args.display_name is None):
				pass
			else:
				entry["displayname"] = args.display_name
			if (args.email is None):
				pass
			else:
				entry["email"] = args.email
			data["users"][args.login_name] = entry
			data_write(args.binary_file_path, args.conf_file_path, args.user_file_path, data)
	elif (args.command == "remove"):
		if (args.login_name is None):
			raise ValueError("name required")
		else:
			if (not (args.login_name in data["users"])):
				raise ValueError("no such user")
			else:
				del data["users"][args.login_name]
				data_write(args.binary_file_path, args.conf_file_path, args.user_file_path, data)
	else:
		raise NotImplementedError()


main()
