import os
import time
from threading import Lock

from typing import Optional

from systools import sys_parsefilepath

from application.model import UpdateType
from application.model.configuration import Configurations
from application.model.customexception import DisabledFunctionality
from application.repository.pos import BinaryUpdaterPOSRepository
from application.service import UpdaterService
from application.util import (
    LoggerUtil,
    settings
)

GENESIS_PATH = "../../genesis"

logger = LoggerUtil.get_logger_name(__name__)


class ChromeUpdaterService(object):
    def __init__(
        self,
        configs,            # type: Configurations
        working_on_update,  # type: Lock
        updater_service,    # type: UpdaterService
        pos_repository      # type: BinaryUpdaterPOSRepository
    ):
        # type: (...) -> None # noqa

        self.update_type = UpdateType.binaries
        self.update_name = self.update_type.name

        self.enabled = configs.updaters[self.update_name].enabled
        self.chrome_url = configs.updaters[self.update_name].chrome_url
        self.chrome_version_url = configs.updaters[self.update_name].chrome_version_url
        self.chrome_update_enabled = configs.updaters[self.update_name].chrome_update_enabled

        data_path = sys_parsefilepath("{HVDATADIR}")
        self.genesis_path = os.path.join(data_path, GENESIS_PATH)

        self.working_on_update = working_on_update
        self.updater_service = updater_service
        self.pos_repository = pos_repository

    def update_chrome(
        self,
        validate_version=False,     # type: bool
        restart_hv=False            # type: bool
    ):
        # type: (...) -> None

        if not self.chrome_update_enabled:
            return

        settings.set_update_control(self.update_name, None)
        if not self.enabled:
            settings.set_update_control(self.update_name, "$UPDATE_DISABLED")
            raise DisabledFunctionality()

        logger.info("Checking for Chrome")

        try:
            settings.set_update_control(self.update_name, "$UPDATING_CHROME")
            settings.set_update_control("updating", True)
            chrome_zip_path = self.genesis_path + '\\chrome.zip'
            chrome_version_file_exists = os.path.exists(os.path.join(self.genesis_path, "chrome", "chromeVersion.txt"))
            if not os.path.exists(self.genesis_path + '\\chrome') or not chrome_version_file_exists:
                validate_version = False

            if not validate_version:
                settings.set_update_control(self.update_name, "$GETTING_NEW_CHROME")
                logger.info("Downloading Chrome without validation")
                self._download_chrome(
                    file_path=chrome_zip_path,
                    restart_hv=restart_hv
                )
            else:
                logger.info("Downloading chromeVersion from server")
                settings.set_update_control(self.update_name, "$GETTING_CHROME_VERSION")
                chrome_version_server = self.pos_repository.download_from_url(
                    url=self.chrome_version_url
                )
                if chrome_version_server.status_code != 200:
                    logger.info("Error downloading Chrome")
                    raise

                with open(os.path.join(self.genesis_path, "chrome", "chromeVersion.txt"), 'r') as f:
                    chrome_version_installed = f.read().replace("\n", "")

                chrome_version_server = str(chrome_version_server.content)
                if chrome_version_server != chrome_version_installed:
                    settings.set_update_control(self.update_name, "$GETTING_NEW_CHROME")
                    logger.info("Downloading new Chrome version {}".format(chrome_version_server))
                    self._download_chrome(
                        file_path=chrome_zip_path,
                        new_chrome_version=chrome_version_server
                    )
                return
        except Exception as e:
            logger.error("Error trying to download Chrome {}".format(e))
            settings.set_update_control("updating", False)
            raise e
        settings.set_update_control("updating", False)

    def _download_chrome(
        self,
        file_path,               # type: str
        new_chrome_version="",   # type: str
        restart_hv=False         # type: bool
    ):
        # type: (...) -> None

        response = self.pos_repository.download_chrome_and_save(
            download_url=self.chrome_url,
            file_path=file_path
        )
        if response:
            path_to_extract = self.genesis_path + "\\chrome"
            self._do_backup(path=path_to_extract)

            try:
                settings.set_update_control(self.update_name, "$UNZIPPING_CHROME")
                self.updater_service.extract_zip(
                    zip_name=file_path,
                    path_to_extract=path_to_extract
                )
                self.updater_service.rename_folder(
                    original_path=path_to_extract + "\\chrome",
                    renamed_path=path_to_extract + "\\windows-x86"
                )
                if not new_chrome_version:
                    logger.info("Downloading Chrome version file")
                    new_chrome_version = self.pos_repository.download_from_url(url=self.chrome_version_url)
                    new_chrome_version = new_chrome_version.content if new_chrome_version.status_code == 200 else ""
                    with open(os.path.join(self.genesis_path, "chrome", "chromeVersion.txt"), 'w') as f:
                        f.write(new_chrome_version)
                self.updater_service.delete_directory(path=path_to_extract + "_backup")
                self.updater_service.delete_file(path=file_path)
            except (Exception,):
                logger.error("Error updating Chrome - Doing Rollback")
                self._do_backup(
                    path=path_to_extract,
                    restore=True
                )

            if restart_hv:
                logger.info("Starting Hypervisor to apply new Chrome")
                self.pos_repository.common_pos_repository.restart_hv(
                    event_type="POS_UPDATER_CHROME",
                )
                time.sleep(5)

            logger.info("Chrome update fully concluded with success!")
        else:
            logger.exception("Error downloading Chrome")

    def _do_backup(
        self,
        path,           # type: str
        restore=False   # type: Optional[bool]
    ):
        # type: (...) -> None

        from_path = path
        to_path = path + "_backup"

        if restore:
            from_path = path + "_backup"
            to_path = path
            self.updater_service.delete_directory(path=to_path)

        if not os.path.exists(from_path):
            return

        self.updater_service.rename_folder(
            original_path=from_path,
            renamed_path=to_path
        )
