# SPDX-FileCopyrightText: 2022 Chris Heywood
#
# SPDX-License-Identifier: GPL-3.0-or-later

from gi.repository import GLib, GObject

from typing import Callable, Optional
import unittest
import warnings

from iotas.attachment import Attachment
from iotas.batch_exporter import BatchExporter
from iotas.default_note_exporter import DefaultNoteExporter
from iotas.note import Note
from iotas.note_exporter import NoteExporter

warnings.filterwarnings("ignore", "version")


class _MockNoteExporter(NoteExporter):

    __gsignals__ = {
        # str: single export out path
        "finished": (GObject.SignalFlags.RUN_FIRST, None, (str,)),
        # str: reason
        "failed": (GObject.SignalFlags.RUN_FIRST, None, (str,)),
    }

    note: Note
    out_format: str
    file_extension: str
    supporting_tex: bool
    allow_missing_images: bool
    user_location: Optional[str]

    export_calls: int = 0

    def export(
        self,
        note: Note,
        out_format: str,
        file_extension: str,
        supporting_tex: bool,
        allow_missing_images: bool = False,
        user_location: Optional[str] = None,
    ) -> None:
        self.note = note
        self.out_format = out_format
        self.file_extension = file_extension
        self.supporting_tex = supporting_tex
        self.allow_missing_images = allow_missing_images
        self.user_location = user_location
        self.export_calls += 1
        self.emit("finished", "")


class Test(unittest.TestCase):

    exporter: BatchExporter
    note_exporter: _MockNoteExporter

    load_content_calls: int
    download_attachment_calls: int

    def test_check_and_transfer_missing_attachments(self) -> None:
        exporter = self.__reset()
        note1 = Note(new_note=True)
        note1.id = 1
        note1.content = "![](image.png)"
        note1.title = "Test Note Title"

        note2 = Note(new_note=True)
        note2.id = 2
        note2.content = "simple"
        note2.title = "Test Note Second"

        exporter.set_notes([note1, note2])

        self.__call_and_wait(exporter.check_and_transfer_missing_attachments)
        self.assertEqual(self.load_content_calls, 0)
        self.assertEqual(self.download_attachment_calls, 1)
        self.assertEqual(self.note_exporter.export_calls, 0)

    def test_check_and_transfer_missing_attachments_no_attachments(self) -> None:
        exporter = self.__reset()
        note1 = Note(new_note=True)
        note1.id = 1
        note1.content = "plain"
        note1.title = "Test Note Title"

        exporter.set_notes([note1])

        self.__call_and_wait(exporter.check_and_transfer_missing_attachments)
        self.assertEqual(self.load_content_calls, 0)
        self.assertEqual(self.download_attachment_calls, 0)
        self.assertEqual(self.note_exporter.export_calls, 0)

    def test_check_and_transfer_missing_attachments_content_load(self) -> None:
        exporter = self.__reset()
        note1 = Note(new_note=True)
        note1.id = 1
        note1.content = None
        note1.title = "Test Note Title"
        self.assertFalse(note1.content_loaded)

        exporter.set_notes([note1])

        self.__call_and_wait(exporter.check_and_transfer_missing_attachments)
        self.assertEqual(self.load_content_calls, 1)
        self.assertEqual(self.download_attachment_calls, 0)
        self.assertEqual(self.note_exporter.export_calls, 0)
        self.assertTrue(note1.content_loaded)

    def test_start_export(self) -> None:
        exporter = self.__reset()
        note1 = Note(new_note=True)
        note1.id = 1
        note1.content = "plain"
        note1.title = "Test Note Title"

        note2 = Note(new_note=True)
        note2.id = 2
        note2.content = "simple"
        note2.title = "Test Note Second"

        exporter.set_notes([note1, note2])
        exporter.out_format = "md"
        exporter.file_extension = "md"
        exporter.supporting_tex = False
        exporter.location = "a-path"

        self.__call_and_wait(exporter.start_export)
        self.assertEqual(self.load_content_calls, 0)
        self.assertEqual(self.download_attachment_calls, 0)
        self.assertEqual(self.note_exporter.export_calls, 2)

        self.assertEqual(self.note_exporter.out_format, "md")
        self.assertEqual(self.note_exporter.file_extension, "md")
        self.assertEqual(self.note_exporter.supporting_tex, False)
        self.assertTrue(
            self.note_exporter.user_location
            in ["a-path/Test Note Title.md", "a-path/Test Note Second.md"]
        )
        self.assertEqual(self.note_exporter.allow_missing_images, False)

    def __reset(self) -> BatchExporter:
        self.note_exporter = _MockNoteExporter()
        self.exporter = BatchExporter(
            self.note_exporter, self.__mock_download_attachment, self.__mock_load_content
        )
        self.exporter.connect(
            "finished-attachment-detection-transfer-run", self.__on_finished_attachment_run
        )
        self.exporter.connect("batch-finished", self.__on_batch_finished)
        self.exporter.connect("export-failed", self.__on_export_failed)
        self.load_content_calls = 0
        self.download_attachment_calls = 0
        return self.exporter

    def __call_and_wait(self, call: Callable, *args, **kwargs) -> None:
        context = GLib.MainContext.default()
        self.success = False
        self.awaiting_signal = True
        call(*args, **kwargs)
        while self.awaiting_signal:
            context.iteration(False)

    def __on_finished_attachment_run(self, _obj: GObject.Object, count: int) -> None:
        self.awaiting_signal = False
        self.success = count == 0
        print("success?")

    def __on_batch_finished(self, *args) -> None:
        self.awaiting_signal = False
        self.success = True
        print("success?")

    def __on_export_failed(self, _obj: DefaultNoteExporter, error) -> None:
        print(f"Failed: {error}")
        self.awaiting_signal = False

    def __mock_download_attachment(self, attachment: Attachment) -> bool:
        self.download_attachment_calls += 1
        return True

    def __mock_load_content(self, note: Note) -> None:
        self.load_content_calls += 1
        note.content = "mock"
