# -*- coding: utf-8 -*-
"""
/***************************************************************************
 PluginSearchCodeDialog
 This plugin connects to the NDFF api
                             -------------------
        begin                : 2021-12-20
        git sha              : $Format:%H$
        copyright            : (C) 2021 by Zuidt
        email                : richard@zuidt.nl
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""

import os

from qgis.PyQt import (
    uic,
)
from qgis.PyQt.QtWidgets import (
    QDialog,
    QTableView,
    QAbstractItemView,
    QDialogButtonBox,
)
from qgis.PyQt.QtCore import (
    Qt,
    QUrl,
    QItemSelection,
)
from qgis.PyQt.QtGui import (
    QGuiApplication,
    QStandardItem,
    QStandardItemModel,
    QDesktopServices,
)
from .ext.ndff.connector import (
    NdffConnector,
)

import logging
from . import LOGGER_NAME
log = logging.getLogger(LOGGER_NAME)

# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
FORM_CLASS, _ = uic.loadUiType(os.path.join(
    os.path.dirname(__file__), 'ndffc_plugin_search_code_dialog_base.ui'))


class PluginSearchCodeDialog(QDialog, FORM_CLASS):
    """
    Dialog to
    - search the api for Ndff Codes, See: https://accapi.ndff.nl/codes/v2/
    OR
    - search for RELATED codes (eg the abundance_value uri's for Tansley/Londo etc.)

    Possible types to search for:

    abundancies
    categories
    codes
    activities
    biotopes
    determinationmethods
    dwellings
    extrainfo
    lifestages
    origins
    protocols
    roletypes
    sexes
    speciesgroups
    subjecttypes
    surveymethods
    taxa
    """

    WARNING = 'Selecteer een URI uit de lijst, of gebruik "Annuleer/Cancel"'

    def __init__(self, parent=None):
        """Constructor."""
        super(PluginSearchCodeDialog, self).__init__(parent)
        # Set up the user interface from Designer through FORM_CLASS.
        # After self.setupUi() you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)

        self.connector = None
        self.current_ndff_field = None
        self.related_to_uri = None

        self.btn_search_api.clicked.connect(self.search_code)
        self.result_model = QStandardItemModel()
        self.table_results.setModel(self.result_model)

        # select full row upon clicking on a cell / row
        self.table_results.setSelectionBehavior(QTableView.SelectRows)
        # only one row selectable at a time
        self.table_results.setSelectionMode(QAbstractItemView.SingleSelection)
        # make table cells non-editable:
        self.table_results.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.table_results.selectionModel().selectionChanged.connect(self.result_row_clicked)
        # search while typing is too slow...?
        # self.le_search_value.textChanged.connect(self.search_api)
        self.le_search_value.textChanged.connect(self.search_value_changed)
        self.le_search_value.returnPressed.connect(self.search_code)
        # trying to NOT close the dialog upon enter pressed
        self.buttonBox.button(QDialogButtonBox.Ok).setDefault(False)
        self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(False)

        # self.lbl_selected.setTextInteractionFlags(Qt.TextSelectableByMouse)  # to make sure the user can copy/paste this uri
        self.lbl_selected.linkActivated.connect(self.open_in_browser)

    def setup_gui(self, connector: NdffConnector, observation_field: str, search_value: str, abundance_schema_uri: str = None, description_string: str = 'Mapping maken voor het veld:'):
        self.lbl_description.setText(description_string)
        self.connector = connector
        # log.debug(f'Setup search code gui: api={connector.get_api()}, field={observation_field}, search_text={search_value}')
        self.current_ndff_field = observation_field
        self.le_search_value.setText(f'{search_value}')
        self.lbl_field.setText(f'({self.current_ndff_field })')
        self.lbl_api_result.setText('')

        # opening the dialog, try to lookup uri's based on current search_value
        # BUT if the field to search in is abundance_value, AND there is a valid abundance_schema_uri, search for
        # related codes in the abundance_schema_uri domain
        if self.current_ndff_field == 'abundance_value' and abundance_schema_uri:
            self.lbl_field.setText(f'({self.current_ndff_field }), binnen "{abundance_schema_uri}" de waarde "{search_value}"')
            self.related_to_uri = abundance_schema_uri
            self.search_code()  # first search without clicking 'search' button
        else:
            self.related_to_uri = None
            self.search_code()  # first search without clicking 'search' button

        self.result_model.setHeaderData(0, Qt.Horizontal, "Naam")
        # self.result_model.setHeaderData(1, Qt.Horizontal, "Wetenschappelijke Naam")
        self.result_model.setHeaderData(2, Qt.Horizontal, "NDFF-URI (Identity)")
        self.result_model.setHeaderData(3, Qt.Horizontal, "Link")
        self.result_model.setHeaderData(4, Qt.Horizontal, "Omschrijving")

    def search_code(self):

        QGuiApplication.setOverrideCursor(Qt.WaitCursor)

        # cleanup
        self.lbl_api_result.setText('')
        self.result_model.removeRows(0, self.result_model.rowCount())
        self.le_selected_uri.clear()
        self.lbl_selected.setText('')

        page_size = 100
        max_hits = 500  # NOTE it is best if max_hits is an exact multiple of page_size, as else the result will be more than max_hits

        if self.related_to_uri:
            # RELATED CODES
            search_text = None
            if self.le_search_value.text() not in ('', None):
                search_text = self.le_search_value.text()
                log.debug(f'Searching related codes: "{self.related_to_uri=}"')
            codes = self.connector.get_related_codes(self.related_to_uri, search_text=search_text)
        else:
            # 'NORMAL' CODES
            search_type = self.current_ndff_field
            search_field = 'name'
            log.debug(f'Searching codes: "{self.le_search_value.text()}" "{search_type=}" "{search_field=}"')
            codes = self.connector.search_codes(search_text=self.le_search_value.text(), search_type=search_type, search_field=search_field, max_hits=max_hits, page_size=page_size)

        if len(codes) == 0:
            log.debug('-> No results...')
            self.lbl_api_result.setText(f'De zoekterm "{self.le_search_value.text()}" leverde GEEN resultaten, probeer een andere zoekterm.')

        if len(codes) >= max_hits:
            log.info(f'Search for codes got to many hits, limiting to {len(codes)}')
            self.lbl_api_result.setText(f'De zoekterm "{self.le_search_value.text()}" leverde meer dan {max_hits} hits en is afgebroken, verfijn uw zoekterm.')

        for item in codes:
            name = QStandardItem(item['name'])
            identity = QStandardItem(item['identity'])  # this is the visual identity, NOT the internal identity (the 'self'-uri)
            if 'scientific' in item:
                scientific = QStandardItem(item['scientific'])
            else:
                scientific = QStandardItem('')
            if item['_links']['self']['href']:
                self_href = QStandardItem(item['_links']['self']['href'])
            else:
                self_href = QStandardItem('')
            if 'description' in item:
                description = QStandardItem(item['description'])
            else:
                description = QStandardItem('')
            self.result_model.appendRow([name, scientific, identity, self_href, description])

        # some pretty-up the table Headers:
        if self.current_ndff_field == 'taxon':
            self.result_model.setHeaderData(1, Qt.Horizontal, "Wetenschappelijke Naam")
            self.table_results.setColumnWidth(1, 200)
        else:
            self.result_model.setHeaderData(1, Qt.Horizontal, "")
            self.table_results.setColumnWidth(1, 20)
        # hide te column with numbers:
        # self.table_results.verticalHeader().setVisible(False)
        # make the width's of the columns fit as good as possible:
        self.table_results.resizeColumnToContents(0)
        self.table_results.resizeColumnToContents(1)
        self.table_results.resizeColumnToContents(2)
        self.table_results.resizeColumnToContents(3)
        self.table_results.setColumnWidth(0, 200)
        self.table_results.horizontalHeader().setStretchLastSection(True)
        # back to normal cursor...
        QGuiApplication.restoreOverrideCursor()

    def result_row_clicked(self, selection_idx: QItemSelection):
        # log.debug(selection_idx)  # QItemSelection
        # log.debug(selection_idx.first())  # QItemSelection
        # log.debug(selection_idx.indexes()[0])  # QModelIndex
        # log.debug(f' row:{selection_idx.indexes()[0].row()} column: {selection_idx.indexes()[0].column()}')
        self.lbl_selected.setText('')
        if selection_idx.isEmpty():
            return
        if selection_idx.indexes()[0].isValid() and selection_idx.indexes()[0].siblingAtColumn(2).isValid():
            name = self.result_model.data(selection_idx.indexes()[0].siblingAtColumn(0))
            uri = self.result_model.data(selection_idx.indexes()[0].siblingAtColumn(2))
            link = self.result_model.data(selection_idx.indexes()[0].siblingAtColumn(3))
            description = self.result_model.data(selection_idx.indexes()[0].siblingAtColumn(4))
            self.le_selected_uri.setText(uri)
            result_txt = f'{name}  - - {uri} - - <a href="{link}">{link}</a><br/>{description}'
            self.lbl_selected.setText(result_txt)
        else:
            log.debug('Row clicked but non valid index...')  # should never happen

    def search_value_changed(self):
        self.lbl_api_result.setText('')
        if self.le_search_value.text() == '':
            self.result_model.removeRows(0, self.result_model.rowCount())
            self.lbl_selected.setText('')

    def accept(self) -> None:
        log.debug('OK, clicked (Search Code Dialog)')
        if self.le_selected_uri.text() in ('', self.WARNING):
            self.le_selected_uri.setText(self.WARNING)
        else:
            self.done(QDialog.Accepted)

    def reject(self) -> None:
        # we clean up the le_selected_uri in case there is the old warning
        # clients of this dialog will grab the text from le_selected_uri...
        if self.le_selected_uri.text() == self.WARNING:
            self.le_selected_uri.setText('')
        self.done(QDialog.Rejected)

    @staticmethod
    def open_in_browser(link: str):
        QDesktopServices.openUrl(QUrl(link))
