# Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html

from __future__ import annotations

import json
from collections.abc import Callable

from google.protobuf.json_format import MessageToDict

import aqt
from anki.cards import Card, CardId
from anki.errors import NotFoundError
from anki.lang import without_unicode_isolation
from aqt.qt import *
from aqt.utils import (
    add_close_shortcut,
    disable_help_button,
    qconnect,
    restoreGeom,
    saveGeom,
    setWindowIcon,
    tooltip,
    tr,
)
from aqt.webview import AnkiWebView, AnkiWebViewKind


class CardInfoDialog(QDialog):
    TITLE = "browser card info"
    GEOMETRY_KEY = "revlog"
    silentlyClose = True

    def __init__(
        self,
        parent: QWidget | None,
        mw: aqt.AnkiQt,
        card: Card | None,
        on_close: Callable | None = None,
        geometry_key: str | None = None,
        window_title: str | None = None,
    ) -> None:
        super().__init__(parent)
        self.mw = mw
        self._on_close = on_close
        self.GEOMETRY_KEY = geometry_key or self.GEOMETRY_KEY
        if window_title:
            self.setWindowTitle(window_title)
        self._setup_ui(card.id if card else None)
        self.show()

    def _setup_ui(self, card_id: CardId | None) -> None:
        self.mw.garbage_collect_on_dialog_finish(self)
        self.setMinimumSize(400, 300)
        disable_help_button(self)
        restoreGeom(self, self.GEOMETRY_KEY, default_size=(800, 800))
        add_close_shortcut(self)
        setWindowIcon(self)

        self.web: AnkiWebView | None = AnkiWebView(
            kind=AnkiWebViewKind.BROWSER_CARD_INFO
        )
        self.web.setVisible(False)
        self.web.load_sveltekit_page(f"card-info/{card_id}")
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.web)
        buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Close)
        buttons.setContentsMargins(10, 0, 10, 10)
        layout.addWidget(buttons)
        qconnect(buttons.rejected, self.reject)

        self.copy_debug_info = QShortcut(  # type: ignore
            "ctrl+c", self, activated=lambda: self.copy_card_info(card_id)
        )

        self.setLayout(layout)

    def copy_card_info(self, card_id: CardId | None) -> None:
        if self.web and self.web.selectedText():
            self.web.onCopy()
            return
        if card_id is None:
            return
        assert aqt.mw.col.db, tr.errors_inconsistent_db_state()

        proto_info = aqt.mw.col.card_stats_data(card_id)
        info = MessageToDict(proto_info)

        card = aqt.mw.col.get_card(card_id)

        revlog = aqt.mw.col.db.execute(
            f"SELECT * FROM revlog WHERE cid == {card_id} ORDER BY id DESC"
        )
        deck = aqt.mw.col.decks.get(card.did) or dict()
        config = aqt.mw.col.decks.get_config(deck.get("conf", -1)) or dict()

        info["deck"] = deck
        info["config"] = config

        info["config"].pop("name", None)
        info["deck"].pop("name", None)
        info["deck"].pop("desc", None)
        info["deck"].pop("usn", None)
        info.pop("usn", None)
        info.pop("cardType", None)
        info.pop("notetype", None)
        info.pop("preset", None)

        info["cardRow"] = aqt.mw.col.db.execute(
            f"SELECT * FROM cards WHERE id == {card_id} ORDER BY id DESC"
        )[0]

        new_revlog = [
            {"row": revlog, "info": card_info_review}
            for revlog, card_info_review in zip(revlog, info.get("revlog", []))
        ]
        info["revlog"] = new_revlog
        info["rollover"] = aqt.mw.col.get_config("rollover")

        clipboard = QApplication.clipboard()
        assert clipboard is not None
        clipboard.setText(json.dumps(info, indent=2))

        tooltip(tr.about_copied_to_clipboard())

    def update_card(self, card_id: CardId | None) -> None:
        try:
            self.mw.col.get_card(card_id)
        except NotFoundError:
            card_id = None

        assert self.web is not None
        self.web.eval(f"anki.updateCard('{card_id}');")

    def reject(self) -> None:
        if self._on_close:
            self._on_close()
        assert self.web is not None
        self.web.cleanup()
        self.web = None
        saveGeom(self, self.GEOMETRY_KEY)
        return QDialog.reject(self)


class CardInfoManager:
    """Wrapper class to conveniently toggle, update and close a card info dialog."""

    def __init__(self, mw: aqt.AnkiQt, geometry_key: str, window_title: str):
        self.mw = mw
        self.geometry_key = geometry_key
        self.window_title = window_title
        self._card: Card | None = None
        self._dialog: CardInfoDialog | None = None

    def show(self) -> None:
        if self._dialog:
            self._dialog.activateWindow()
            self._dialog.raise_()
        else:
            self._dialog = CardInfoDialog(
                None,
                self.mw,
                self._card,
                self._on_close,
                self.geometry_key,
                self.window_title,
            )

    def set_card(self, card: Card | None) -> None:
        self._card = card
        if self._dialog:
            self._dialog.update_card(card.id if card else None)

    def close(self) -> None:
        if self._dialog:
            self._dialog.reject()

    def _on_close(self) -> None:
        self._dialog = None


class BrowserCardInfo(CardInfoManager):
    def __init__(self, mw: aqt.AnkiQt):
        super().__init__(
            mw,
            "revlog",
            without_unicode_isolation(
                tr.card_stats_current_card(context=tr.qt_misc_browse())
            ),
        )


class ReviewerCardInfo(CardInfoManager):
    def __init__(self, mw: aqt.AnkiQt):
        super().__init__(
            mw,
            "reviewerCardInfo",
            without_unicode_isolation(
                tr.card_stats_current_card(context=tr.decks_study())
            ),
        )


class PreviousReviewerCardInfo(CardInfoManager):
    def __init__(self, mw: aqt.AnkiQt):
        super().__init__(
            mw,
            "previousReviewerCardInfo",
            without_unicode_isolation(
                tr.card_stats_previous_card(context=tr.decks_study())
            ),
        )
