import asyncio
from collections.abc import Awaitable
from typing import cast

from gi.repository import Gio, GLib, GObject

from morphosis import PANDOC_DEFAULTS_FILE


class ConversionError(Exception):
    """Raised if the Pandoc conversion failed."""


class Format(GObject.Object):
    """A document format."""

    __gtype_name__ = "Format"

    name = GObject.Property(type=str)
    pandoc_id = GObject.Property(type=str)
    extension = GObject.Property(type=str)
    supports_font_style = GObject.Property(type=bool, default=False)


class Document(GObject.Object):
    """A document data object."""

    __gtype_name__ = "Document"

    file = GObject.Property(type=Gio.File)
    path = GObject.Property(type=str)
    name = GObject.Property(type=str)
    icon = GObject.Property(type=Gio.Icon)

    conversion_failed = GObject.Signal()
    conversion_finished = GObject.Signal()

    async def set_from_file(self, file: Gio.File) -> None:
        """Set properties from a `Gio.File`."""
        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.file = file
        self.path = path
        self.name = file_info.get_display_name()
        self.icon = file_info.get_icon()

    async def convert(
        self,
        *,
        output_file: Gio.File,
        document_format: Format,
        font_style: str,
    ) -> None:
        """Convert the document."""
        process = await asyncio.create_subprocess_exec(
            "pandoc",
            self.path,
            f"--defaults={PANDOC_DEFAULTS_FILE}",
            f"--variable=mainfont:{font_style}",
            f"--to={document_format.pandoc_id}",
            f"--output={output_file.get_path()}",
        )

        _stdout, stderr = await process.communicate()
        if stderr:
            self.emit("conversion-failed")
            raise ConversionError(stderr)

        self.file = output_file
        self.emit("conversion-finished")
