from gi.repository import GLib, GObject

from collections import OrderedDict
import logging

from packaging.version import Version, InvalidVersion

import iotas.config_manager
from iotas.config_manager import HeaderBarVisibility
from iotas import const
from iotas.note import DirtyFields
from iotas.note_database import NoteDatabase
from iotas.ui_utils import have_window_with_width, is_likely_mobile_device


class MigrationAssistant(GObject.Object):

    def __init__(self, db: NoteDatabase) -> None:
        self.__db = db
        self.__migrations = OrderedDict()
        self.__migrations["0.1.14"] = self.__make_keeping_webkit_in_memory_default
        self.__migrations["0.9.0"] = self.__v090_preference_migrations

        self.__ui_dependent_migrations = OrderedDict()
        self.__ui_dependent_migrations["0.9.0"] = self.__set_formatting_bar_style_based_on_device

        version = iotas.config_manager.get_last_launched_version()
        try:
            self.__previous_version = Version(version)
        except InvalidVersion:
            logging.warning(f"Unexpected last version {version}")
            self.__previous_version = None

    def migrate(self) -> None:
        current_version = self.__get_current_version()
        if self.__previous_version != current_version:
            if self.__previous_version is not None:
                if current_version > self.__previous_version:
                    logging.info("Updating to v{}".format(current_version))
                    self.__run_migrations(self.__migrations)
                    self.__wait_and_run_ui_dependent_migrations()
                else:
                    logging.info(
                        "Downgrading to v{}? Not running migrations.".format(current_version)
                    )
                self.__regenerate_excerpts()
            iotas.config_manager.set_last_launched_version(str(current_version))

    def __wait_and_run_ui_dependent_migrations(self) -> None:

        # Another location (ala FirstStart) using this pattern as a result of not yet
        # discovering the clean way to be notified when a window has obtained its initial size
        def wait_and_run():
            if not have_window_with_width():
                GLib.timeout_add(250, wait_and_run)
            else:
                self.__run_migrations(self.__ui_dependent_migrations)

        wait_and_run()

    def __run_migrations(self, migrations: OrderedDict) -> None:
        for version, function in migrations.items():
            if Version(version) > self.__previous_version:
                function()

    def __get_current_version(self) -> Version:
        version = const.VERSION
        # Handle development suffixes
        if "-" in version:
            version = version.split("-")[0]
        return Version(version)

    def __regenerate_excerpts(self) -> None:
        notes = self.__db.get_all_notes(load_content=True)
        for note in notes:
            if note.regenerate_excerpt():
                changed_fields = DirtyFields()
                changed_fields.excerpt = True
                self.__db.persist_note_selective(note, changed_fields)

    def __make_keeping_webkit_in_memory_default(self) -> None:
        logging.info("Setting WebKit process to be retained in memory after first use")
        iotas.config_manager.set_markdown_keep_webkit_process(True)

    def __v090_preference_migrations(self) -> None:
        logging.info("Running v0.9.0 preference migrations")
        if not iotas.config_manager.get_markdown_syntax_hightlighting_enabled():
            logging.info(
                "Syntax highlighting was previous disabled, migrating to syntax detection enabled "
                "with unstyled theme"
            )
            iotas.config_manager.set_markdown_detect_syntax(True)
            iotas.config_manager.set_editor_theme("iotas-unstyled")

        if iotas.config_manager.get_hide_editor_headerbar():
            visibility = HeaderBarVisibility.AUTO_HIDE
        elif iotas.config_manager.get_hide_editor_headerbar_when_fullscreen():
            visibility = HeaderBarVisibility.AUTO_HIDE_FULLSCREEN_ONLY
        else:
            visibility = HeaderBarVisibility.ALWAYS_VISIBLE
        logging.info(
            f"Migrated previous header bar preferences to '{str(visibility)}' header bar visibility"
        )
        iotas.config_manager.set_editor_header_bar_visibility(visibility)

    def __set_formatting_bar_style_based_on_device(self) -> None:
        if is_likely_mobile_device():
            logging.info("Likely on mobile device, setting formatting bar to stay visible")
            visibility = HeaderBarVisibility.ALWAYS_VISIBLE
        else:
            logging.info("Likely not on mobile device, setting formatting bar to auto hide")
            visibility = HeaderBarVisibility.AUTO_HIDE
        iotas.config_manager.set_editor_formatting_bar_visibility(visibility)
