#!/usr/bin/env python3

# imports.
import os, sys
from dev0s.shortcuts import *

# settings.
ALIAS = "torvpn"
OS = dev0s.defaults.operating_system(supported=["macos"])

# installation.
if "-c" not in sys.argv:
	os.system("clear")
if "command not found" in dev0s.utils.__execute_script__("brew -h"):
	os.system('/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" && brew doctor')
	if "command not found" in dev0s.utils.__execute_script__("brew -h"):
		raise ValueError("HomeBrew is not installed.")
if "command not found" in dev0s.utils.__execute_script__("tor -h"):
	os.system('brew install tor tor-browser')
	if "command not found" in dev0s.utils.__execute_script__("tor -h"):
		raise ValueError("Tor is not installed.")
if "--create-alias" in sys.argv:
	dev0s.defaults.alias(alias=ALIAS, executable=__file__)
	sys.exit(1)

# the vpn object class.
class VPN(object):
	# networksetup: https://superuser.com/questions/316502/how-to-change-proxy-setting-using-command-line-in-mac-os
	def __init__(self):
		self.tor = Tor()
		self.services = self.__get_services__()
		self.server = "127.0.0.1"
		self.port = 9050
	def install(self):
		for service in self.services:
			response = self.__installed__(service=service)
			if not response.success: 
				dev0s.response.log(response=response)
				return response
			elif not response["installed"]:
				dev0s.response.log(f"Installing the proxy settings of service {service}.")
				response = self.__set_proxy__(service=service, server=self.server, port=self.port)
				dev0s.response.log(response=response)
				if not response.success: return response
				response = self.__disable_proxy__(service=service)
				if not response.success: 
					dev0s.response.log(response=response)
					return response
			else:
				dev0s.response.log(f"The proxy settings of service {service} are already installed.")
		return dev0s.response.success("Succesfully installed the tor VPN.", log_level=0)
	def uninstall(self):
		for service in self.services:
			response = self.__installed__(service=service)
			if not response.success: 
				dev0s.response.log(response=response)
				return response
			elif response["installed"]:
				dev0s.response.log(f"Uninstalling the proxy settings of service {service}.")
				response = self.__set_proxy__(service=service, server="", port=0)
				dev0s.response.log(response=response)
				if not response.success: return response
				dev0s.response.log(f"Disabling proxy service {service}.")
				response = self.__disable_proxy__(service=service)
				dev0s.response.log(response=response)
				if not response.success: return response
			elif response["enabled"]:
				dev0s.response.log(f"Disabling proxy service {service}.")
				response = self.__disable_proxy__(service=service)
				dev0s.response.log(response=response)
				if not response.success: return response
		return dev0s.response.success("Succesfully uninstalled the tor VPN.", log_level=0)
	def enable(self, check_tor=True):
		if check_tor:
			self.__check_tor__()
		for service in self.services:
			response = self.__enable_proxy__(service=service)
			dev0s.response.log(response=response)
			if not response.success: return response
		return dev0s.response.success("Succesfully enabled the tor VPN.", log_level=0)
	def disable(self):
		for service in self.services:
			response = self.__disable_proxy__(service=service)
			dev0s.response.log(response=response)
			if not response.success: return response
		return dev0s.response.success("Succesfully disabled the tor VPN.", log_level=0)
	def status(self, silent=False):
		status = {}
		for service in self.services:
			response = self.__get_proxy__(service=service)
			if not response.success: 
				dev0s.response.log(response=response)
				return response
			status[service.lower().replace(" ","-")] = response["info"]
		if not silent:
			for service, info in status.items():
				print(f"Service {service}:")
				for key, value in info.items():
					print(f" * {key}: {value}")
		response = dev0s.network.info()
		if not response.success:  return response
		del response["message"] ; del response["error"] ; del response["success"]
		network = response
		if not silent:
			print(f"Network:")
			for key, value in network.items():
				print(f" * {key}: {value}")
		return dev0s.response.success("Succesfully retieved the status of the tor VPN.", {
			"status":status,
			"network":network,
		}, log_level=0)
	# system functions.
	def __disable_proxy__(self, service=None):
		output = dev0s.utils.__execute_script__(f"networksetup -setsocksfirewallproxystate '{service}' off")
		if "** Error: Unable to find item in network database." in output:
			return dev0s.response.error(f"Service [{service}] does not exist.")
		response = self.__get_proxy__(service=service)
		if not response.success: return response
		success = response["info"]["enabled"] == False
		if success:
			return dev0s.response.success(f"Succesfully disabled proxy service {service}.")
		else:
			return dev0s.response.error(f"Failed to disable proxy service {service}.")
	def __enable_proxy__(self, service=None):
		output = dev0s.utils.__execute_script__(f"networksetup -setsocksfirewallproxystate '{service}' on")
		if "** Error: Unable to find item in network database." in output:
			return dev0s.response.error(f"Service [{service}] does not exist.")
		response = self.__get_proxy__(service=service)
		if not response.success: return response
		success = response["info"]["enabled"] == True
		if success:
			return dev0s.response.success(f"Succesfully enabled proxy service {service}.")
		else:
			return dev0s.response.error(f"Failed to enable proxy service {service}.")
	def __get_proxy__(self, service=None):
		output = dev0s.utils.__execute_script__(f"networksetup -getsocksfirewallproxy '{service}'")
		if "** Error: Unable to find item in network database." in output:
			return dev0s.response.error(f"Service [{service}] does not exist.")
		info = {}
		for line in output.split("\n"):
			if ":" in line:
				before,after = line.split(": ")
				if after == "No": after = False
				elif after == "Yes": after = True
				elif after == "": after = None
				elif after in ["Null", "None", "null", "none"]: after = True
				else:
					try: after = int(after)
					except: a=1

				info[before.lower().replace(" ","_")] = after
		return dev0s.response.success(f"Succesfully retrieved the {service} proxy settings.", {"info":info})
	def __set_proxy__(self, service=None, server=None, port=None, authentication=False, username=None, password=None):
		command = f"networksetup -setsocksfirewallproxy '{service}' {server} {port}"
		if authentication:
			return dev0s.response.error("Not supported yet.")
			if username == None or password == None:
				return dev0s.response.error("Authentication is enabled and the username / password is null.")
			command += f" 1 {username} {password}"
		output = dev0s.utils.__execute_script__(command)
		if "** Error: Unable to find item in network database." in output:
			return dev0s.response.error(f"Service [{service}] does not exist.")
		response = self.__get_proxy__(service=service)
		if not response.success: return response
		success = response["info"]["server"] == server and response["info"]["port"] == port
		if success:
			return dev0s.response.success(f"Succesfully set the proxy setting of service {service}.")
		else:
			return dev0s.response.error(f"Failed set the proxy setting of service.")
	def __get_services__(self):
		output = dev0s.utils.__execute_script__("networksetup -listallnetworkservices")
		if "An asterisk (*) denotes that a network service is disabled." in output: output = output.split("An asterisk (*) denotes that a network service is disabled.\n")[1]
		services = []
		for line in output.split("\n"):
			if line not in [""]:
				services.append(line)
		return services
	def __installed__(self, service=None):
		response = self.__get_proxy__(service=service)
		if not response.success: return response
		return dev0s.response.success(f"Succesfully checked the {service} proxy installation.", {
			"installed":response["info"]["server"] == self.server and response["info"]["server"] == self.port,
			"enabled":response["info"]["enabled"],
		})
	def __check_tor__(self):
		if not self.tor.running():
			dev0s.response.log("&RED&Tor is not running.&END&")
			dev0s.response.log(f"Execute [ $ {ALIAS} --start] to start tor.")
			#dev0s.response.log("&RED&Execute your original command in a different terminal.&END&")
			#dev0s.response.log("&ORANGE&Starting tor for you ...&END&")
			#self.tor.start()
			sys.exit(1)

# the tor object class.
class Tor(object):
	def __init__(self):
		a=1
	def start(self):
		os.system("tor")
	def running(self):
		return "Congratulations" in dev0s.utils.__execute_script__("curl --socks5 localhost:9050 --socks5-hostname localhost:9050 -s https://check.torproject.org/ | cat | grep -m 1 Congratulations | xargs")
		#

# the cli object class.
class CLI(dev0s.cli.CLI):
	def __init__(self):
		dev0s.cli.CLI.__init__(self,
			modes={
				"--start":"Start tor.",
				"--install":"Install the tor VPN settings.",
				"--uninstall":"Uninstall the tor VPN settings.",
				"--enable":"Enable the tor VPN settings.",
				"--disable":"Disable the tor VPN settings.",
				"--status":"Status report of the the tor VPN settings.",
				"--create-alias":"Install the tor VPN library.",
				"--help":"Show documentation.",
			},
			options={
				"-c":"Do not clear the console.",
			},
			alias=ALIAS,)
	def start(self):
		self.arguments.check()
		vpn = VPN()
		
		# help.
		if self.arguments.present(["-h", "--help"]):
			self.docs()

		# install.
		elif self.arguments.present("--start"):
			vpn.enable(check_tor=False)
			vpn.tor.start()
			vpn.disable()

		# install.
		elif self.arguments.present("--install"):
			vpn.install()

		# uninstall.
		elif self.arguments.present("--uninstall"):
			vpn.uninstall()

		# enable.
		elif self.arguments.present("--enable"):
			vpn.enable()

		# disable.
		elif self.arguments.present("--disable"):
			vpn.disable()

		# status.
		elif self.arguments.present("--status"):
			vpn.status()
		
		# invalid.
		else: self.invalid()

# main.
if __name__ == "__main__":
	cli = CLI()
	cli.start()