from parallels.core import messages
import logging
import os
import shutil
from contextlib import closing

from parallels.core.utils.common import mkdir_p, is_run_on_windows
from parallels.core.actions.base.common_action import CommonAction
from parallels.core.plesk_backup import plesk_backup_xml

logger = logging.getLogger(__name__)


class Unpack(CommonAction):
	def get_description(self):
		return "Unpack backups"

	def get_failure_message(self, global_context):
		return "Failed to unpack backups"

	def run(self, global_context):
		"""
		:type global_context: parallels.core.global_context.GlobalMigrationContext
		"""
		for plesk_id in global_context.source_plesks:
			session_files = global_context.session_files
			for backup_filename in [
				session_files.get_path_to_converted_plesk_backup(plesk_id),
				session_files.get_path_to_raw_plesk_backup(plesk_id)
			]:
				self._extract_plesk_backup(global_context, backup_filename)

	@staticmethod
	def _extract_plesk_backup(global_context, backup_filename):
		extracted_files = []
		if os.path.exists(backup_filename):
			migrator_server = global_context.migrator_server
			extract_dirname = migrator_server.get_session_file_path(
				os.path.join('unpacked', os.path.basename(backup_filename))
			)
			logger.info(
				messages.EXTRACTING_FILES_FROM_BACKUP_S_S,
				backup_filename, extract_dirname
			)
			mkdir_p(extract_dirname)
			archive = plesk_backup_xml.PleskBackupContainer(backup_filename).archive
			for member in archive.get_members():
				if member.is_file:
					extract_filename = os.path.join(extract_dirname, member.name) 
					extracted_files.append(extract_filename)
					mkdir_p(os.path.dirname(extract_filename))
					if is_run_on_windows():
						# Workaround issue with Windows maximum full path
						# length, which is equal to 260 symbols. We exceed it
						# on domains with long names, that are owned by
						# customers owned by resellers
						# Details are there:
						# http://stackoverflow.com/questions/14075465/copy-a-file-with-a-too-long-path-to-another-directory-in-python
						# http://stackoverflow.com/questions/1880321/why-does-the-260-character-path-length-limit-exist-in-windows
						target_path = "\\\\?\\" + os.path.abspath(
							extract_filename
						)
					else:
						target_path = extract_filename
					with closing(archive.extract(member.name)) as source, closing(open(target_path, 'wb')) as target:
						shutil.copyfileobj(source, target)
			archive.close()
		return extracted_files
