import sys
import os

from py_module.whiptail import Whiptail
import py_module.docker as docker
from time import sleep
import asyncio
import subprocess
import shutil

from .base import Base

class Docker_installer(Base):

    def postload(self):
        asyncio.get_event_loop().run_until_complete(self.install_docker())

    def copy_stack(self, stack:list = []):
        self.logger.debug(f'Начинаю копирование файлов согласно списку:\n{stack}');
        for copy in stack:
            for root, dirs, files in os.walk(copy[0]):
                for file in files:
                    if not os.path.isdir(copy[1]):
                        os.makedirs(copy[1], exist_ok=True)
                    shutil.copy2(f'{root}/{file}', copy[1]);
                    self.logger.debug(f'Файл {root}/{file} успешно скопирован.')
        self.logger.info(f"Копирование файлов завершено.");

    async def install_docker(self):
        self.logger.debug('Запускаю "which docker" для поиска исполняемого файла docker');
        docker_bin = subprocess.Popen(['which', 'docker'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.read().decode('utf-8').rstrip('\n');
        try:
            self.logger.debug(f'Запускаю "{docker_bin} version" для проверки среды docker');
            docker_engine = subprocess.Popen([docker_bin, 'version'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.read().decode('utf-8');
            self.logger.info('Среда docker присутствует в системе');
        except:
            if self.form.yesno('Среда контейнеризации Docker не найдена.\nДальнейшая работа невозможна без установки Docker и Docker Compose.\n\nПроизвести установку Docker и Docker Compose Plugin?', default = "yes"):
                self.logger.warning('Среда контейнеризации Docker не найдена. Произвожу попытку установки Docker и Docker Compose Plugin');
                try:
                    self.logger.debug('Создаю директории "/etc/docker" и "/usr/local/lib/docker/cli-plugins"');
                    stack=[
                        (f'{sys.path[0]}/runtime/docker', "/usr/bin"),
                        (f'{sys.path[0]}/runtime/configs', "/etc/docker"),
                        (f'{sys.path[0]}/runtime/docker-plugins', "/usr/local/lib/docker/cli-plugins"),
                        (f'{sys.path[0]}/runtime/docker-plugins', "/usr/lib/docker/cli-plugins"),
                        (f'{sys.path[0]}/runtime/services', "/etc/systemd/system")
                    ];
                    self.copy_stack(stack = stack);
                except:
                    self.logger.critical(f'Произошла ошибка при копировании docker-файлов.');
                    await self.msgbox_async(f'Произошла ошибка при копировании.\n\nОбратитесь в техподдержку продукта для дальнейшей консультации. Приложите log-файл:\n{sys.path[0]}/support_logs/installer.log');
                    sys.exit();
                try:
                    self.logger.info('Запускаю службы docker и containerd');

                    self.logger.debug('Запускаю "systemctl daemon-reload"');
                    with subprocess.Popen(['systemctl', 'daemon-reload'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as run:
                        stdout, stderr = run.communicate();
                        if stderr: self.logger.warning(stderr.decode('utf-8'));

                    self.logger.debug('Запускаю "systemctl enable containerd"');
                    with subprocess.Popen(['systemctl', 'enable', 'containerd'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as run:
                        stdout, stderr = run.communicate();
                        if stdout: self.logger.info(stdout.decode('utf-8'));
                        if stderr: self.logger.warning(stderr.decode('utf-8'));

                    self.logger.debug('Запускаю "systemctl enable docker.socket"');
                    with subprocess.Popen(['systemctl', 'enable', 'docker.socket'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as run:
                        stdout, stderr = run.communicate();
                        if stdout: self.logger.info(stdout.decode('utf-8'));
                        if stderr: self.logger.warning(stderr.decode('utf-8'));

                    self.logger.debug('Запускаю "systemctl enable docker"');
                    with subprocess.Popen(['systemctl', 'enable', 'docker'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as run:
                        stdout, stderr = run.communicate();
                        if stdout: self.logger.info(stdout.decode('utf-8'));
                        if stderr: self.logger.warning(stderr.decode('utf-8'));

                    self.logger.debug('Запускаю "systemctl start containerd"');
                    with subprocess.Popen(['systemctl', 'start', 'containerd'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as run:
                        stdout, stderr = run.communicate();
                        if stdout: self.logger.info(stdout.decode('utf-8'));
                        if stderr: self.logger.warning(stderr.decode('utf-8'));

                    self.logger.debug('Запускаю "systemctl start docker.socket"');
                    with subprocess.Popen(['systemctl', 'start', 'docker.socket'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as run:
                        stdout, stderr = run.communicate();
                        if stdout: self.logger.info(stdout.decode('utf-8'));
                        if stderr: self.logger.warning(stderr.decode('utf-8'));

                    self.logger.debug('Запускаю "systemctl start docker"');
                    with subprocess.Popen(['systemctl', 'start', 'docker'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as run:
                        stdout, stderr = run.communicate();
                        if stdout: self.logger.info(stdout.decode('utf-8'));
                        if stderr: self.logger.warning(stderr.decode('utf-8'));
                except:
                    self.logger.critical(f"Произошла ошибка при инициализации служб Docker'а.")
                    await self.msgbox_async(f'Произошла ошибка при запуске служб Docker. Обратитесь в техподдержку продукта для дальнейшей консультации. Приложите log-файл:\n{sys.path[0]}/support_logs/installer.log');
                    sys.exit();
                else:
                    docker_bin = subprocess.Popen(['which', 'docker'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.read().decode('utf-8').rstrip('\n');
                    self.logger.info('Среда контейнеризации успешно установлена');
                    await self.msgbox_async(f'Среда контейнеризации успешно установлена');
            else:
                self.form.msgbox(f'Завершаю работу инсталлера');
                sys.exit();
        try:

            self.logger.debug(f'Запускаю "{docker_bin} compose version" для проверки наличия плагина docker-compose');
            docker_engine = subprocess.Popen([docker_bin, 'compose', 'version'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.read().decode('utf-8');
            if not "Docker Compose version" in docker_engine:
                raise "Compose not found"
            else:
                self.logger.info('Плагин docker compose присутствует в системе');
        except:
            self.logger.info('Плагин docker compose отсутствует в системе. Произвожу установку.');
            stack=[
                (f'{sys.path[0]}/runtime/docker-plugins', "/usr/lib/docker/cli-plugins"),
                (f'{sys.path[0]}/runtime/docker-plugins', "/usr/local/lib/docker/cli-plugins")
            ];
            self.copy_stack(stack = stack);
            try:
                self.logger.debug(f'Запускаю "{docker_bin} compose version" для проверки установленного плагина docker-compose');
                docker_engine = subprocess.Popen([docker_bin, 'compose', 'version'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.read().decode('utf-8');
                if not "Docker Compose version" in docker_engine:
                    raise "Compose not found"
                else:
                    self.logger.info('Плагин docker compose присутствует в системе');
            except:
                self.logger.error(f'Произошла ошибка при установке Docker Compose Plugin.')
                await self.msgbox_async(f'Произошла ошибка при установке Docker Compose Plugin. Обратитесь в техподдержку продукта Р7-офис');
                sys.exit()
        while True:
            try:
                self.logger.info('Произвожу попытку подключения к Docker API')
                self.dockercli = docker.from_env();
            except:
                self.logger.warn('Не удалось подключиться к Docker API, повторяю попытку через 5 секунд.');
                print ('Не удалось подключиться к Docker API, повторяю попытку через 5 секунд.');
                sleep(5)
                continue
            else:
                self.logger.info('Подключение к Docker API прошло успешно')
                break
