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

from __future__ import annotations

from collections.abc import Sequence

from markdown import markdown

import aqt
import aqt.forms
import aqt.operations
from anki.collection import OpChangesWithCount
from anki.notes import NoteId
from aqt import AnkiQt
from aqt.operations import QueryOp
from aqt.operations.note import find_and_replace
from aqt.operations.tag import find_and_replace_tag
from aqt.qt import *
from aqt.utils import (
    HelpPage,
    disable_help_button,
    openHelp,
    qconnect,
    restore_combo_history,
    restore_combo_index_for_session,
    restore_is_checked,
    restoreGeom,
    save_combo_history,
    save_combo_index_for_session,
    save_is_checked,
    saveGeom,
    showWarning,
    tooltip,
    tr,
)


class FindAndReplaceDialog(QDialog):
    COMBO_NAME = "BrowserFindAndReplace"

    def __init__(
        self,
        parent: QWidget,
        *,
        mw: AnkiQt,
        note_ids: Sequence[NoteId],
        field: str | None = None,
    ) -> None:
        """
        If 'field' is passed, only this is added to the field selector.
        Otherwise, the fields belonging to the 'note_ids' are added.
        """
        super().__init__(parent)
        self.mw = mw
        self.note_ids = note_ids
        self.field_names: list[str] = []
        self._field = field

        if field:
            self._show([field])
        elif note_ids:
            # fetch field names and then show
            QueryOp(
                parent=mw,
                op=lambda col: col.field_names_for_note_ids(note_ids),
                success=self._show,
            ).run_in_background()
        else:
            self._show([])

    def _show(self, field_names: Sequence[str]) -> None:
        # add "all fields" and "tags" to the top of the list
        self.field_names = [
            tr.browsing_all_fields(),
            tr.editing_tags(),
        ] + list(field_names)

        disable_help_button(self)
        self.form = aqt.forms.findreplace.Ui_Dialog()
        self.form.setupUi(self)
        self.setWindowModality(Qt.WindowModality.WindowModal)

        self._find_history = restore_combo_history(
            self.form.find, self.COMBO_NAME + "Find"
        )

        find_completer = self.form.find.completer()
        assert find_completer is not None
        find_completer.setCaseSensitivity(Qt.CaseSensitivity.CaseSensitive)
        self._replace_history = restore_combo_history(
            self.form.replace, self.COMBO_NAME + "Replace"
        )
        replace_completer = self.form.replace.completer()
        assert replace_completer is not None
        replace_completer.setCaseSensitivity(Qt.CaseSensitivity.CaseSensitive)

        if not self.note_ids:
            # no selected notes to affect
            self.form.selected_notes.setChecked(False)
            self.form.selected_notes.setEnabled(False)
        elif self._field:
            self.form.selected_notes.setChecked(False)

        restore_is_checked(self.form.re, self.COMBO_NAME + "Regex")
        restore_is_checked(self.form.ignoreCase, self.COMBO_NAME + "ignoreCase")

        self.form.field.addItems(self.field_names)
        if self._field:
            self.form.field.setCurrentIndex(self.field_names.index(self._field))
        else:
            restore_combo_index_for_session(
                self.form.field, self.field_names, self.COMBO_NAME + "Field"
            )

        qconnect(self.form.buttonBox.helpRequested, self.show_help)

        restoreGeom(self, "findreplace")
        self.show()
        self.form.find.setFocus()

    def accept(self) -> None:
        saveGeom(self, "findreplace")
        save_combo_index_for_session(self.form.field, self.COMBO_NAME + "Field")

        search = save_combo_history(
            self.form.find, self._find_history, self.COMBO_NAME + "Find"
        )
        replace = save_combo_history(
            self.form.replace, self._replace_history, self.COMBO_NAME + "Replace"
        )
        regex = self.form.re.isChecked()
        match_case = not self.form.ignoreCase.isChecked()
        save_is_checked(self.form.re, self.COMBO_NAME + "Regex")
        save_is_checked(self.form.ignoreCase, self.COMBO_NAME + "ignoreCase")

        if not self.form.selected_notes.isChecked():
            # an empty list means *all* notes
            self.note_ids = []

        parent_widget = self.parentWidget()
        assert parent_widget is not None

        # tags?
        if self.form.field.currentIndex() == 1:
            op = find_and_replace_tag(
                parent=parent_widget,
                note_ids=self.note_ids,
                search=search,
                replacement=replace,
                regex=regex,
                match_case=match_case,
            )
        else:
            # fields
            if self.form.field.currentIndex() == 0:
                field = None
            else:
                field = self.field_names[self.form.field.currentIndex()]

            op = find_and_replace(
                parent=parent_widget,
                note_ids=self.note_ids,
                search=search,
                replacement=replace,
                regex=regex,
                field_name=field,
                match_case=match_case,
            )

        def on_success(changes: OpChangesWithCount) -> None:
            if self.note_ids:
                message = tr.findreplace_notes_updated(
                    changed=changes.count, total=len(self.note_ids)
                )
            else:
                message = tr.browsing_notes_updated(count=changes.count)
            tooltip(message, parent=self.parentWidget())
            super(FindAndReplaceDialog, self).accept()

        op.success(on_success)
        op.failure(lambda err: showWarning(markdown(str(err))))
        op.run_in_background()

    def show_help(self) -> None:
        openHelp(HelpPage.BROWSING_FIND_AND_REPLACE)
