from collections.abc import Awaitable
from gettext import gettext as _
from pathlib import Path
from typing import Any, cast

from gi.repository import Adw, Gio, GLib, GObject, Gtk

from morphosis import tasks
from morphosis.config import APP_ID, PREFIX
from morphosis.document import (
    HELP_URL,
    INPUT_FILTER,
    OUTPUT_FORMATS_MODEL,
    ConversionError,
    Document,
    Format,
)

from .drop_overlay import DropOverlay


@Gtk.Template(resource_path=f"{PREFIX}/window.ui")
class Window(Adw.ApplicationWindow):
    """The main application window."""

    __gtype_name__ = "Window"

    toast_overlay: Adw.ToastOverlay = Gtk.Template.Child()
    navigation_view: Adw.NavigationView = Gtk.Template.Child()

    format_row: Adw.ComboRow = Gtk.Template.Child()
    font_style_group: Adw.ToggleGroup = Gtk.Template.Child()

    document = GObject.Property(type=Document)
    input_formats = GObject.Property(type=Gtk.Filter, default=INPUT_FILTER)
    output_formats = GObject.Property(type=Gio.ListModel, default=OUTPUT_FORMATS_MODEL)
    selected_format = GObject.Property(type=Format)
    selected_font_style = GObject.Property(type=str)

    def __init__(self, **kwargs: Any) -> None:
        super().__init__(**kwargs)

        # https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/3933
        schema = Gio.Settings.new(APP_ID)
        schema.bind(
            "export-format", self.format_row, "selected", Gio.SettingsBindFlags.DEFAULT
        )
        schema.bind(
            "font-style", self.font_style_group, "active", Gio.SettingsBindFlags.DEFAULT
        )

        self.add_action_entries(  # type: ignore[no-untyped-call]
            (
                ("open", lambda *_: tasks.create(self._open_document())),
                ("convert", lambda *_: tasks.create(self._convert_document())),
                ("launch-help", lambda *_: Gtk.UriLauncher.new(HELP_URL).launch(self)),
                (
                    "launch-file",
                    lambda _action, path, _data: Gtk.FileLauncher.new(
                        Gio.File.new_for_path(path.unpack())
                    ).launch(self),
                    "s",
                ),
            )
        )

    async def _open_document(self) -> None:
        dialog = Gtk.FileDialog(default_filter=INPUT_FILTER)

        try:
            file = await cast(Awaitable[Gio.File], dialog.open(self))
        except GLib.Error:
            return

        await self._load_document(file)

    async def _load_document(self, file: Gio.File) -> None:
        if not (path := file.get_path()):
            raise ValueError(path)

        file_info = await cast(
            Awaitable[Gio.FileInfo],
            file.query_info_async(
                "standard::display-name,standard::icon",
                Gio.FileQueryInfoFlags.NONE,
                GLib.PRIORITY_DEFAULT,
            ),
        )

        self.document = Document(
            path=path,
            name=file_info.get_display_name(),
            icon=file_info.get_icon(),
        )

        self.navigation_view.push_by_tag("document")

    async def _convert_document(self) -> None:
        suffix = f".{self.selected_format.extension}"
        initial_name = str(Path(self.document.name).with_suffix(suffix))
        dialog = Gtk.FileDialog(initial_name=initial_name)

        try:
            output_file = await cast(Awaitable[Gio.File], dialog.save(self))
        except GLib.Error:
            return

        if not (output_path := output_file.get_path()):
            return

        self.navigation_view.push_by_tag("converting")

        try:
            await self.document.convert(
                output_path=output_path,
                document_format=self.selected_format,
                font_style=self.selected_font_style,
            )
        except ConversionError:
            toast = Adw.Toast(
                title=_("Conversion failed"),
                button_label=_("Help"),
                action_name="win.launch-help",
            )
        else:
            toast = Adw.Toast(
                title=_("Document converted"),
                button_label=_("Open"),
                action_name="win.launch-file",
                action_target=GLib.Variant.new_string(output_path),
            )

        self.navigation_view.pop_to_tag("start")
        self.toast_overlay.dismiss_all()
        self.toast_overlay.add_toast(toast)

    @Gtk.Template.Callback()
    def _open_dropped_file(self, _drop_overlay: DropOverlay, file: Gio.File) -> None:
        tasks.create(self._load_document(file))
