# -*- coding: utf-8 -*-

# Copyright 2013-2016 Mir Calculate. http://www.calculate-linux.org
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

import sys
from os import path
from calculate.core.server.func import Action, Tasks
from calculate.lib.cl_lang import setLocalTranslate, getLazyLocalTranslate
from calculate.lib.utils.files import FilesError
from calculate.desktop.desktop import DesktopError
from calculate.client.client import ClientError
from calculate.lib.cl_template import TemplatesError
from calculate.lib.utils.files import isMount

_ = lambda x: x
setLocalTranslate('cl_client3', sys.modules[__name__])
__ = getLazyLocalTranslate(_)


class ClClientSyncLoginAction(Action):
    """
    Синхронизировать локальный профиль с удаленным, подключить удаленные
    ресурсы пользователя
    """
    native_error = (FilesError, ClientError, DesktopError, TemplatesError)

    successMessage = None
    failedMessage = None
    interruptMessage = __("Synchronization manually interrupted")

    tasks = [
        # подключить удаленный ресурс домена
        {'name': 'mount_remote',
         'method': 'Client.mountRemoteRes(cl_remote_pw,cl_client_remote_path,'
                   'cl_remote_host)',
         'condition': lambda Get: (Get('cl_remote_host') and
                                   Get('os_remote_auth') and
                                   not isMount(Get('cl_client_remote_path'))),
         },
        # check on domain user
        {'name': 'domain_user',
         'condition': lambda Get: (Get('os_remote_auth') and
                                   Get('cl_remote_host') and
                                   Get('desktop.ur_domain_set') == 'on'),
         'else_message': __("The local profile will be used")
         },
        {'name': 'domain_user:create_home',
         'message': __("Creating the home directory for {ur_login}"),
         'method': 'Client.createUserDirectory(ur_home_path,ur_uid,'
                   'ur_gid)',
         'condition': lambda Get: not path.exists(Get('ur_home_path'))
         },
        # password in kernel key
        {'name': 'domain_user:check_password',
         'condition': lambda Get: Get('desktop.ur_password'),
         'else_error': __("User password not found")
         },
        {'name': 'ecryptfs',
         'message': __("Mounting encrypted data"),
         'method': 'Desktop.createCryptDir(ur_login,ur_uid,ur_gid,'
                   'ur_home_path,True)',
         'condition': lambda Get: (Get('desktop.ur_home_crypt_set') == 'on' and
                                   Get('install.cl_autologin') != Get(
                                       'ur_login'))
         },
        {'name': 'domain_user:add_to_cache',
         'essential': False,
         'method': 'Client.cAddUserToCache(ur_login,desktop.ur_password)',
         'failed_warning': __("Unable to cache user info")
         },
        # подключить удаленные ресурсы пользователя
        {'name': 'domain_user:mount_resources',
         'message': __("Mounting user resources"),
         'method': 'Client.mountUserDomainRes(ur_login,'
                   'desktop.ur_password,'
                   'ur_uid,ur_gid,"unix","share","homes","ftp")',
         },
        # проверка на попытку открыть вторую сессию для этого пользователя
        {'name': 'two_session',
         'error':
             __("A second X session cannot be opened for user {ur_login}."),
         'condition': lambda dv: (dv.Get('ur_login') in
                                  dv.Get('desktop.cl_desktop_online_user') and
                                  int(dv.Select(
                                      'desktop.cl_desktop_online_count',
                                      where='desktop.cl_desktop_online_user',
                                      eq=dv.Get('ur_login'), limit=1) > 1) and
                                  dv.Get('cl_client_sync') == 'on')
         },
        {'name': 'domain_user:domain_sync',
         'method': 'Client.setSyncStatus(ur_home_path,ur_uid,ur_gid,"process")',
         'condition': lambda Get: Get('cl_client_sync') == 'on'
         },
        # подключить профиль пользователя на удаленном домене
        # если на нем находится актуальный профиль
        {'name': 'domain_sync:repl_profile',
         'method': 'Client.mountUserDomainRes(ur_login,desktop.ur_password,'
                   'ur_uid,ur_gid,"unix","remote_profile")',
         'condition': lambda Get: Get('cl_replication_host')
         },
        # отправить команду архивирования профиля на удаленном домене
        # если он новее локального профиля
        {'name': 'repl_profile:pack_remote',
         'method': 'Client.packRemote("remote_profile",'
                   'cl_client_local_sync_time,cl_client_pack_time,'
                   'cl_client_profile_name,ur_uid,ur_gid)',
         'condition': lambda Get: Get('cl_client_sync_replication_set') == 'on',
         'else_message': __("The local user profile does not "
                            "need to be synchronized with the remote domain")
         },
        # монтируем профиль локального домена, если локальный профиль
        # старее удаленного доменного или актуальный профиль
        {'name': 'domain_sync:mount_local',
         'method': 'Client.mountUserDomainRes(ur_login,desktop.ur_password,'
                   'ur_uid,ur_gid,"unix")',
         # нет более ранних ошибок и локальный профиль нуждается
         # в синхронизации с удаленным или профиль на локальном домене
         'depend': (Tasks.success_all('mount_resources') &
                    (Tasks.hasnot('repl_profile') |
                     Tasks.has('pack_remote')))
         },
        # синхронизируем с профилем локального домена
        {'name': 'mount_local!:sync_local',
         'method': 'Client.syncLoginProfile(cl_remote_host,ur_uid,'
                   'ur_gid,ur_home_path,"unix",cl_client_profile_name)',
         'condition': lambda Get: Get('cl_client_sync_local_set') == 'on',
         'else_message': __("The local user profile does not "
                            "need to be synchronized with the local domain")
         },
        # ошибка синхронизации с локальным доменом
        {'name': 'local_sync_error',
         'warning': __(
             "Error synchronizing with the local server {cl_remote_host}"),
         'depend': Tasks.failed_one_of("mount_local", "sync_local")
         },
        # подключить удаленный профиль пользователя с "репликации"
        {'name': 'repl_profile:repeat_repl_profile',
         'method': 'Client.mountUserDomainRes(ur_login,desktop.ur_password,'
                   'ur_uid,ur_gid,"remote_profile")',
         },
        # ждать архив от удаленного домена
        {'name': 'pack_remote:wait_archive',
         'message': __("Packing the archive on the server"),
         'method': 'Client.waitingArchFile(cl_client_pack_time,'
                   'cl_client_profile_name,"remote_profile")',
         'failed_warning': __("Failed to find the profile "
                              "archive of {cl_replication_host}")
         },
        # распаковать архив из удаленного домена и удалить
        # файлы которые отсутствуют в удаленном профиле
        {'name': 'wait_archive:unpack_profile',
         'message': __("Unpacking the profile"),
         'method': 'Client.unpackArch(ur_home_path,cl_client_pack_time,'
                   'cl_client_profile_name,"remote_profile")',
         'failed_warning': __("Failed to unpack")
         },
        # удалить временные архивы
        {'name': 'clean_archfiles',
         'method': 'Client.cleanArchs(cl_client_pack_time,'
                   'cl_client_profile_name,"remote_profile")',
         'failed_warning': __("Unable to remove useless files"),
         'essential': False,
         'depend': Tasks.has('pack_remote')
         },
        # синхронизировать профиль с удаленным доменом в случае ошибки
        {'name': 'repl_profile:sync_remote',
         'method': 'Client.syncLoginProfile(cl_replication_host,ur_uid,'
                   'ur_gid,ur_home_path,"remote_profile",'
                   'cl_client_profile_name)',
         'depend': Tasks.failed_one_of('pack_remote', 'mount_local',
                                       'sync_local',
                                       'wait_archive', 'unpack_profile'),
         'condition': lambda Select: isMount(
             Select('cl_client_user_mount_path',
                    where='cl_client_user_mount_name', eq='remote_profile',
                    limit=1))
         },
        # если синхронизация с удаленным доменом прошла с ошибкой
        # синхронизировать локальный профиль с локальным доменом
        # как запасной профиль
        {'name': 'pack_remote:fallback_warning',
         'warning': __("Error synchronizing with the "
                       "{cl_replication_host} remote server"),
         'depend': ~Tasks.success_one_of('unpack_profile', 'sync_remote')
         },
        {'name': 'pack_remote:fallback_sync',
         'method': 'Client.syncLoginProfile(cl_remote_host,ur_uid,'
                   'ur_gid,ur_home_path,"unix",cl_client_profile_name)',
         'depend': ~Tasks.success_one_of('unpack_profile', 'sync_remote')
         },
        # отключить профиль на удаленном домене
        {'name': 'repl_profile!:umount_remote_profile',
         'method': 'Client.umountRemoteUserRes(True,"remote_profile")',
         },
        # сообщение о том, что будет использоваться запасной профиль
        # с локального домена
        {'name': 'fallback_sync!:fallback_success',
         'message': __("Got a user fallback profile from the "
                       "{cl_remote_host} domain")
         },
        # ошибка синхронизации профиль не готов! к использованию
        {'name': 'failed',
         'error': __("Failed to get the user profile from the domain"),
         'method': 'Client.setSyncStatus(ur_home_path,ur_uid,ur_gid,"error")',
         'depend': Tasks.failed_all('sync_remote', 'sync_local',
                                    'fallback_sync') |
                   (Tasks.hasnot('domain_sync') & Tasks.failed()) |
                   Tasks.failed_one_of('mount_resources', 'two_session')
         },
        # распаковать ссылки
        {'name': 'domain_sync:unpack_links',
         'method': 'Client.unpackLinks(ur_home_path)',
         'failed_warning': __("Failed to unpack the links archive"),
         'depend': Tasks.hasnot('failed')
         },
        # синхронизация профиля завершилась успешно
        {'name': 'domain_sync:success_sync',
         'message': __("User profile fetched from the domain"),
         'method': 'Client.setSyncStatus(ur_home_path,ur_uid,ur_gid,"success")',
         'depend': Tasks.success_all('sync_remote', 'unpack_links') |
                   Tasks.success()
         },
        # во время синхронизации профиля произошли ошибки, которые не
        # гарантируют целостность профиля
        {'name': 'domain_sync:error_sync',
         'warning': __("User profile modifications will not "
                       "be saved to the domain"),
         'method': 'Client.setSyncStatus(ur_home_path,ur_uid,ur_gid,"error")',
         'depend': Tasks.hasnot('success_sync', 'failed')
         },
        # отключить ресурсы в случае ошибки
        {'name': 'umount_remote_res',
         'message': __("Umounting user resources"),
         'method': 'Client.umountUserRes(desktop.ur_mount_dirs)',
         'depend': Tasks.has('failed')
         }
    ]


class ClClientSyncLogoutAction(Action):
    """
    Синхронизировать локальный профиль с удаленным, отключить удаленные
    ресурсы пользователя
    """
    native_error = (FilesError, ClientError, DesktopError, TemplatesError)

    successMessage = None
    failedMessage = None
    interruptMessage = __("Synchronization manually interrupted")

    tasks = [
        # проверка доменный ли пользователь
        {'name': 'domain_user',
         'condition': lambda Get: (Get('os_remote_auth') and
                                   Get('cl_remote_host') and
                                   Get('desktop.ur_domain_set') == 'on'),
         'else_message': __("The local profile will be used")
         },
        # проверка на попытку отключить ресурсы пользователя в X сессии
        {'name': 'domain_user:in_xsession',
         'error': __("User {ur_login} is already on the X session"),
         'condition': lambda Get: Get('ur_login') in
                                  Get('desktop.cl_desktop_online_user'),
         },
        # проверить наличие домашней директории
        {'name': 'domain_user:check_homedir',
         'condition': lambda Get: path.exists(Get('ur_home_path')),
         'else_error': __("Home directory {ur_home_path} not found"),
         },
        # проверить наличие подключенных ресурсов
        {'name': 'domain_user:check_mount',
         'condition': lambda Get: any(x and isMount(x)
                                      for x in
                                      Get('cl_client_user_mount_path')),
         'else_error': __("Remote user resources not found")
         },
        # установить время выхода из сеанса
        {'name': 'domain_user:set_logout_date',
         'method': 'Client.setLogoutDate(ur_home_path,ur_uid,ur_gid)'
         },
        # выполнять ли синхронизацию
        {'name': 'domain_user:domain_sync',
         'condition': lambda Get: Get('cl_client_sync_status') == 'success' and
                                  Get('cl_client_sync') == 'on',
         'else_warning': __("The profile will not be uploaded to the domain")
         },
        # переместить файлы из профиля в Moved
        {'name': 'domain_user:move_home_dir',
         'message': __("Moving non-profile files to the Home/Moved directory"),
         'method': 'Client.moveHomeDir(ur_home_path,"Moved","homes",'
                   'cl_moved_skip_path)',
         'condition': lambda Get: Get('cl_sync_moved_set') == 'on'
         },
        # архивировать симлинки
        {'name': 'domain_sync:tar_symlinks',
         'method': 'Client.tarSymLinks(ur_uid,ur_gid,ur_home_path,'
                   'cl_sync_del_path,cl_sync_skip_path)',
         'failed_error': _("Failed to make a links archive")
         },
        # закачать профиль пользователя в домен
        {'name': 'domain_sync:sync_logout',
         'method': 'Client.syncLogoutProfile(cl_remote_host,ur_uid,'
                   'ur_gid,ur_home_path,"unix",cl_client_profile_name,'
                   'cl_client_symlinks)',
         },
        # удалить файлы, которые могут помешать следующему входу в сеанс
        {'name': 'domain_sync:remove_noise_files',
         'message': __("Removing hindering files"),
         'method': 'Client.removeNoiseFiles(ur_home_path)'
         },
        # удалить "личные" файлы
        {'name': 'domain_sync:remove_private_files',
         'message': __("Removing user private files"),
         'method': 'Client.removePrivateFiles(ur_home_path)'
         },
        # удалить пользовательские ключи ядра
        {'name': 'domain_user!:clear_user_key',
         'message': __("Clearing user keys"),
         'method': 'Client.clearUserKey(ur_login)'
         },
        # отключить пользовательские ресурсы
        {'name': 'check_mount!:umount_user_res',
         'message': __("Umounting user resources"),
         'method': 'Client.umountUserRes(desktop.ur_mount_dirs)',
         },
        # установить статус синхронизации
        {'name': 'domain_sync:success_sync',
         'message': __("Modified user profile saved in the domain"),
         'method': 'Client.setSyncStatus(ur_home_path,ur_uid,ur_gid,'
                   '"success_logout")',
         'depend': Tasks.success_all('sync_logout', 'check_mount')
         },
        {'name': 'domain_sync:failed',
         'error': __("Modified user profile saved in the domain with errors"),
         'method': 'Client.setSyncStatus(ur_home_path,ur_uid,ur_gid,'
                   '"error")',
         'depend': Tasks.hasnot('success_sync')
         },
        {'name': 'umount_allres',
         'method': 'Desktop.umountUserRes(ur_home_path)',
         }
    ]
