from parallels.core import messages
import ntpath
import logging

from parallels.core.utils.yaml_utils import read_yaml, write_yaml
from parallels.core.windows_rsync import RsyncInstaller, RsyncClient, RsyncServer, RsyncConfig, RsyncControl
from parallels.core.utils.common import generate_random_password
from parallels.core.utils.common.threading_utils import synchronized_by_args

logger = logging.getLogger(__name__)


class RsyncPool(object):

	def __init__(self, storage_path_prefix):
		self._storage_path_prefix = storage_path_prefix
		self._clients = {}
		self._servers = {}

	@synchronized_by_args
	def get(self, source, target, vhosts_dir=None, source_ip=None):
		"""
		:rtype: parallels.core.windows_rsync.RsyncControl
		"""
		if source is None and source_ip is None:
			raise Exception(messages.UNABLE_GET_RSYNC_SOURCE_SERVER_OBJECT)

		client = self._prepare_client(target)
		server = self._prepare_server(source, vhosts_dir) if source is not None else None

		#  update config with passed vhost_dir
		if server is not None and server.vhosts_dir != vhosts_dir:
			self._update_server_vhosts_dir(source, vhosts_dir)

		return RsyncControl(
			target=target,
			target_rsync_bin=ntpath.join(client.base_path, 'panel-migrator-rsync.exe'),
			source_ip=source.ip() if source is not None else source_ip,
			source_login=server.login if server is not None else None,
			source_password=server.password if server is not None else None,
			server=server
		)

	def stop_all(self):
		for ip in self._servers:
			self._servers[ip].stop()
		self._servers.clear()

	def stop(self, ip, is_terminated=False):
		"""Remove rsync server with given ip from pool
		:type ip: str
		:param is_terminated: if rsync server process was already terminated, no necessary to call stop command,
		so pass True to just remove it from pool
		"""
		if ip not in self._servers:
			return
		if not is_terminated:
			self._servers[ip].stop()
		del self._servers[ip]

	def _prepare_client(self, server):
		ip = server.ip()
		if ip not in self._clients:
			self._clients[ip] = RsyncClient(self._deploy(server))
		return self._clients[ip]

	def _prepare_server(self, server, vhosts_dir):
		ip = server.ip()
		if ip not in self._servers:
			rsync_server = RsyncServer(self._deploy(server), server)

			rsync_config = RsyncConfig(
				client_ips='"*"',
				vhosts_dir=vhosts_dir,
				migrator_dir=server.session_dir(),
				user='admin',
				password=generate_random_password()
			)

			rsync_server.configure(rsync_config)
			rsync_server.restart()

			self._servers[ip] = rsync_server

		return self._servers[ip]

	def _deploy(self, server):
		ip = server.ip()
		storage_path = self._storage_path_prefix + ip
		base_path = read_yaml(storage_path, True)
		if base_path is None:
			logger.debug(messages.DEBUG_INSTALL_RSYNC, ip)
			rsync_installer = RsyncInstaller(server)
			base_path = rsync_installer.install()
			write_yaml(storage_path, base_path)
			logger.debug(messages.DEBUG_RSYNC_INSTALLED_TO % (ip, base_path))

		return base_path

	def _update_server_vhosts_dir(self, server, vhosts_dir):
		self._servers[server.ip()].set_vhosts_dir(vhosts_dir)
