import logging
import subprocess
import sys
import os
from pipes import quote

logger = logging.getLogger(__name__)


class MigrationError(Exception):
	pass


class MigrationConfigurationFileError(Exception):
	pass


class MigrationNoContextError(Exception):
	pass


class MigrationNoRepeatError(Exception):
	"""Exception explicitly stating that there is no sense to repeat failed operation for the object

	For example, if subscription creation failed because such subscription already exists,
	there is no sense to try to create subscription any more times.
	"""
	pass


def run_and_check_local_command(command, stdin_content=None, output_codepage='utf-8'):
	exit_code, stdout_str, stderr_str = local_command_exec(command, stdin_content, output_codepage)
	if exit_code != 0:
		raise MigrationError(
			u"Command '%s' executed locally failed with exit code %d.\nstdout: %s\nstderr:%s" % (
				u" ".join([quote(c) for c in command]),
				exit_code,
				stdout_str,
				stderr_str
			)
		)
	return stdout_str, stderr_str


def local_command_exec(command, stdin_content=None, output_codepage='utf-8', error_policy='strict', env=None):
	if type(command) is list or type(command) is tuple:
		command_str = ' '.join([quote(c) for c in command])
		command_encoded = [c.encode('utf-8') for c in command]
	else:
		command_str = command
		command_encoded = command.encode('utf-8')
	logger.debug(u"Call local command: %s" % command_str)

	command_env = os.environ.copy()
	command_env['LANG'] = 'en_US.utf-8'
	if env is not None:
		command_env.update(env)

	try:
		p = subprocess.Popen(
			command_encoded,
			stdin=subprocess.PIPE,
			stdout=subprocess.PIPE,
			stderr=subprocess.PIPE,
			env=command_env
		)
		stdout_str, stderr_str = [s.decode(output_codepage, error_policy) for s in p.communicate(stdin_content)]
		logger.debug(u"Command stdout: %s", stdout_str)
		logger.debug(u"Command stderr: %s", stderr_str)
		logger.debug(u"Command exit code: %s", p.returncode)
		return p.returncode, stdout_str, stderr_str
	except Exception as e:
		logger.debug(u'Exception:', exc_info=e)
		raise Exception(
			u"Failed to execute local command '%s': %s" % (u" ".join([quote(c) for c in command]), e)
		), None, sys.exc_info()[2]


def version_tuple(version):
	"""Convert string product version to a tuple of ints"""
	return tuple(map(int, version.split('.')))