require "option_parser"

# frequently used functions
opt_authd_login = -> (parser : OptionParser) {
	parser.on "-l LOGIN", "--login LOGIN", "Authd user login." do |login|
		Context.authd_login = login
		Baguette::Log.info "User login for authd: #{Context.authd_login}."
	end
	parser.on "-p PASSWORD", "--password PASSWORD", "Authd user password." do |password|
		Context.authd_pass = password
		Baguette::Log.info "User password for authd: #{Context.authd_pass}."
	end
}

opt_help = -> (parser : OptionParser) {
	parser.on "help", "Prints this help message." do
		puts parser
		exit 0
	end
}

opt_profile = -> (parser : OptionParser) {
	parser.on "-P file", "--profile file", "Read the user profile from a file." do |file|
		Context.user_profile = JSON.parse(File.read file).as_h
		Baguette::Log.info "Reading the user profile: #{Context.user_profile}."
	end
}

opt_email = -> (parser : OptionParser) {
	parser.on "-e email", "--email address", "Email address." do |email|
		Context.email = email
		Baguette::Log.info "Reading the user email address: #{Context.email}."
	end
}


# Unrecognized parameters are used to create commands with multiple arguments.
# Example: user add login email
# Here, login and email are unrecognized arguments.
# Still, the "user add" command expect them.
unrecognized_args_to_context_args = -> (parser : OptionParser, n_expected_args : Int32) {
	# With the right args, these will be interpreted as serialized data.
	parser.unknown_args do |args|
		if args.size != n_expected_args
			Baguette::Log.error "expected number of arguments: #{n_expected_args}, received: #{args.size}"
			Baguette::Log.error "args: #{args}"
			Baguette::Log.error "#{parser}"
			exit 1
		end
		args.each do |arg|
			Baguette::Log.debug "Unrecognized argument: #{arg} (adding to Context.args)"
			if Context.args.nil?
				Context.args = Array(String).new
			end
			Context.args.not_nil! << arg
		end
	end
}

parser = OptionParser.new do |parser|
	parser.banner = "usage: #{PROGRAM_NAME} command help"
	parser.on "-v verbosity", "--verbosity v", "Verbosity. From 0 to 4 (debug)." do |v|
		Baguette::Context.verbosity = v.to_i
		Baguette::Log.info "verbosity = #{v}"
	end
	parser.on "-h", "--help", "Prints this help message." do
		puts "usage: #{PROGRAM_NAME} command help"
		puts parser
		exit 0
	end

	parser.on "bootstrap", "Add the first user (an admin)." do
		parser.banner = "Usage: bootstrap login email [-P profile]"
		Baguette::Log.info "Bootstrapping the first user (admin) to the DB."
		Context.command = "bootstrap"
		opt_profile.call parser
		opt_help.call parser
		# login email
		unrecognized_args_to_context_args.call parser, 2
	end

	parser.on "migration-script", "Add a batch of users from old code base." do
		parser.banner = "usage: migration-script user-db.txt"
		Baguette::Log.info "Add a batch of users."
		Context.command = "migration-script"
		opt_authd_login.call parser
		opt_profile.call parser
		opt_help.call parser
		# user-db.txt
		unrecognized_args_to_context_args.call parser, 1
	end

	parser.on "user", "Operations on users." do
		parser.banner = "Usage: user [add | mod | delete | validate | search | get | recover | register ]"

		parser.on "add", "Add a user to the DB." do
			parser.banner = "usage: user add login email [-P profile] [opt]"
			Baguette::Log.info "Add a user to the DB."
			Context.command = "user-add"
			opt_authd_login.call parser
			opt_profile.call parser
			opt_help.call parser
			# login email
			unrecognized_args_to_context_args.call parser, 2
		end

		parser.on "migrate", "Add a user from old code base." do
			parser.banner = "usage: user migrate login password-hash-brkn"
			Baguette::Log.info "Add a user to the DB from old codebase."
			Context.command = "user-migrate"
			opt_authd_login.call parser
			opt_profile.call parser
			opt_help.call parser
			# login password-hash-brkn
			unrecognized_args_to_context_args.call parser, 2
		end

		parser.on "mod", "Modify a user account." do
			parser.banner = "Usage: user mod userid [-e email|-P profile] [opt]"
			Baguette::Log.info "Modify a user account."
			Context.command = "user-mod"
			opt_authd_login.call parser
			opt_email.call parser
			opt_profile.call parser
			opt_help.call parser
			# userid
			unrecognized_args_to_context_args.call parser, 1
		end

		parser.on "change-password", "Change the password of a user (requires admin)." do
			parser.banner = "Usage: user change-password userid"
			Baguette::Log.info "Change the password of an account."
			Context.command = "user-change-password"
			opt_authd_login.call parser
			opt_email.call parser
			opt_profile.call parser
			opt_help.call parser
			# userid
			unrecognized_args_to_context_args.call parser, 1
		end

		parser.on "delete", "Remove user." do
			parser.banner = "Usage: user delete userid [opt]"
			Baguette::Log.info "Remove user."
			Context.command = "user-delete"
			# You can either be the owner of the account, or an admin.
			opt_authd_login.call parser
			opt_help.call parser
			# userid
			unrecognized_args_to_context_args.call parser, 1
		end

		parser.on "validate", "Validate user." do
			parser.banner = "Usage: user validate login activation-key [opt]"
			Baguette::Log.info "Validate user."
			Context.command = "user-validation"
			# No need to be authenticated.
			opt_help.call parser
			# login activation-key
			unrecognized_args_to_context_args.call parser, 2
		end

		parser.on "get", "Get user info." do
			parser.banner = "Usage: user get login [opt]"
			Baguette::Log.info "Get user info."
			Context.command = "user-get"
			opt_authd_login.call parser
			opt_help.call parser
			# login
			unrecognized_args_to_context_args.call parser, 1
		end

		parser.on "search", "Search user." do
			parser.banner = "Usage: user search login [opt]"
			Baguette::Log.info "Search user."
			Context.command = "user-search"
			opt_authd_login.call parser
			opt_help.call parser
			# login
			unrecognized_args_to_context_args.call parser, 1
		end

		parser.on "recover", "Recover user password." do
			parser.banner = "Usage: user recover login [opt]"
			Baguette::Log.info "Recover user password."
			Context.command = "user-recovery"
			# No need to be authenticated.
			opt_help.call parser
			# login
			unrecognized_args_to_context_args.call parser, 1
		end


		# Do not require to be admin.
		parser.on "register", "Register a user (requires activation)." do
			parser.banner = "Usage: user register login email [-P profile] [opt]"
			Baguette::Log.info "Register a user (requires activation)."
			Context.command = "user-registration"
			opt_profile.call parser
			opt_help.call parser
			# login email
			unrecognized_args_to_context_args.call parser, 2
		end
	end

	parser.on "permission", "Permissions management." do
		parser.banner = "Usage: permissions [check | set]"
		parser.on "set", "Set permissions." do
			parser.banner = <<-END
  usage: permission set user  application     resource  permission
example: permission set 1002  my-application  chat      read

permission list: none read edit admin
END
			Baguette::Log.info "Set permissions."
			Context.command = "permission-set"
			opt_authd_login.call parser
			opt_help.call parser
			# userid application resource permission
			unrecognized_args_to_context_args.call parser, 4
		end

		parser.on "check", "Check permissions." do
			parser.banner = <<-END
  usage: permission check user application    resource
example: permission check 1002 my-application chat

permission list: none read edit admin
END
			Baguette::Log.info "Check permissions."
			Context.command = "permission-check"
			opt_authd_login.call parser
			opt_help.call parser
			# userid application resource
			unrecognized_args_to_context_args.call parser, 3
		end
	end

	parser.unknown_args do |args|
		if args.size > 0
			Baguette::Log.warning "Unknown args: #{args}"
		end
	end

#	parser.on "-X user-password", "--user-password pass", "Read the new user password." do |pass|
#		password = pass
#	end

end


parser.parse