import logging
from parallels.core.utils.database_server_type import DatabaseServerType
from parallels.core.utils.database_utils import list_databases, check_connection

from parallels.core.utils.common import cached, contextmanager
from parallels.core.utils import ssh_utils
from parallels.core.run_command import SSHRunner
from parallels.core.connections.target_servers import TargetServer

logger = logging.getLogger(__name__)


class DatabaseServer(object):
	def description(self):
		if self.host() == 'localhost':
			return "%s database server %s:%s" % (self.type_description, self.ip(), self.port())
		else:
			return "%s database server %s:%s" % (self.type_description, self.host(), self.port())

	@property
	def type_description(self):
		db_server_types = {
			DatabaseServerType.MYSQL: 'MySQL',
			DatabaseServerType.POSTGRESQL: 'PostgreSQL',
			DatabaseServerType.MSSQL: 'MSSQL'
		}
		return db_server_types.get(self.type().lower(), self.type())

	def is_external(self):
		return self.ip() != self.panel_server.ip()

	def is_windows(self):
		raise NotImplementedError()

	def ip(self):
		raise NotImplementedError()

	def type(self):
		raise NotImplementedError()

	def host(self):
		raise NotImplementedError()

	def port(self):
		raise NotImplementedError()

	def user(self):
		raise NotImplementedError()

	@cached
	def password(self):
		raise NotImplementedError()

	@contextmanager
	def runner(self):
		raise NotImplementedError()

	@property
	def panel_server(self):
		return self._panel_server

	def list_databases(self):
		"""List databases on that server.

		Returns list of databases names or None if that function is not supported for that database server type.

		:rtype: set[basestring] | None
		"""
		return list_databases(self)

	def check_connection(self):
		"""Check connection to that database server.

		:raises parallels.core.utils.database_utils.DatabaseServerConnectionException:
		"""
		return check_connection(self)

	@property
	def mysql_bin(self):
		raise NotImplementedError()


class SourceDatabaseServer(DatabaseServer):

	def __init__(self, access_settings, model, panel_server):
		self._access_settings = access_settings
		self._model = model
		self._panel_server = panel_server

	def is_windows(self):
		return self._access_settings.is_windows

	def ip(self):
		return self._access_settings.ip

	def type(self):
		return self._model.dbtype

	def host(self):
		return self._model.host

	def port(self):
		return self._model.port

	def user(self):
		return self._model.login

	@property
	def model(self):
		return self._model

	@property
	def access_settings(self):
		return self._access_settings

	def password(self):
		return self.panel_server.get_database_server_password(self)

	@contextmanager
	def runner(self):
		with ssh_utils.connect(self._access_settings) as ssh_connection:
			yield SSHRunner(
				ssh_connection,
				"source server '%s' ('%s')" % (
					self._access_settings.id,
					self._access_settings.ip
				)
			)


class TargetDatabaseServer(DatabaseServer, TargetServer):
	def type(self):
		return self.db_type

	def user(self):
		return self.login()