from qgis.core import QgsApplication
from qgis.PyQt import QtCore
from qgis.PyQt.QtWidgets import (
    QComboBox,
    QHBoxLayout,
    QInputDialog,
    QMessageBox,
    QToolButton,
    QWidget,
)
from xplordb.datamodel.dataset import Dataset

from openlog.datamodel.connection.interfaces.read_interface import ReadInterface
from openlog.datamodel.connection.interfaces.write_interface import WriteInterface
from openlog.datamodel.connection.openlog_connection import OpenLogConnection


class DatasetSelectionWidget(QWidget):
    def __init__(self, parent: QWidget = None):
        """
        QWidget to display available datasets in OpenLogConnection and import dataset

        Args:
            parent: QWidget parent
        """
        super().__init__(parent)

        self.openlog_connection = None
        self.added_datasets = []
        self.person_code = ""

        layout = QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        # Combobox to define dataset used
        self.dataset_cb = QComboBox()

        # Add button for dataset creation
        layout.addWidget(self.dataset_cb)
        self.create_button = QToolButton(self)
        self.create_button.setIcon(QgsApplication.getThemeIcon("mActionAdd.svg"))
        self.create_button.clicked.connect(self._create_dataset)
        layout.addWidget(self.create_button)
        self.setLayout(layout)

        self.create_button.setEnabled(False)
        self.dataset_cb.currentTextChanged.connect(
            lambda text: self.dataset_changed.emit(text)
        )

    # Signal emitted when dataset is changed.
    dataset_changed = QtCore.pyqtSignal(str)

    def get_dataset_combobox(self) -> QComboBox:
        """
        Get combobox used to display available datasets in openlog connection

        Returns: QComboBox

        """
        return self.dataset_cb

    def set_openlog_connection(self, openlog_connection: OpenLogConnection):
        """
        Define OpenLogConnection used to get available dataset and add dataset to database

        Args:
            openlog_connection: OpenLogConnection
        """
        self.openlog_connection = openlog_connection
        self.create_button.setEnabled(self.openlog_connection is not None)
        self.refresh_available_datasets()

    def import_data(self):
        """
        Import added dataset into openlog database.

        OpenLogConnection.ImportData exception can be raised.

        """
        if self.openlog_connection:
            datasets = [
                Dataset(dataset, self.person_code) for dataset in self.added_datasets
            ]
            try:
                self.openlog_connection.get_write_iface().import_datasets(datasets)
            except WriteInterface.ImportException as exc:
                QMessageBox.warning(self, "Import exception", str(exc))

    def refresh_available_datasets(self) -> None:
        """
        Refresh available dataset names from openlog connection

        """
        self.dataset_cb.clear()
        if self.openlog_connection:
            try:
                self.dataset_cb.addItems(
                    self.openlog_connection.get_read_iface().get_available_dataset_names()
                )
            except ReadInterface.ReadException as exc:
                QMessageBox.warning(self, "Invalid database", str(exc))

    def set_loaded_by_person(self, person_code: str) -> None:
        """
        Define person used for dataset creation and loaded_by parameter definition

        Args:
            person_code: loaded_by definition for dataset creation
        """
        self.person_code = person_code

    def selected_dataset(self) -> str:
        """
        Returns selected dataset

        Returns: selected dataset

        """
        return self.dataset_cb.currentText()

    def _create_dataset(self) -> None:
        """
        Create dataset from a QInputDialog definition

        """

        if self.openlog_connection and self.person_code:
            ok = True
            while ok:
                dataset, ok = QInputDialog.getText(self, "New dataset", "Dataset name")
                if dataset:
                    valid, reason = self._valid_dataset_names(dataset)
                    if valid:
                        self.dataset_cb.addItem(dataset)
                        self.dataset_cb.setCurrentText(dataset)
                        self.added_datasets.append(dataset)
                        break
                    else:
                        QMessageBox.warning(self, "Invalid dataset name", reason)

    def _valid_dataset_names(self, dataset: str) -> (bool, str):
        """
        Check if dataset name is valid

        Args:
            dataset: dataset name

        Returns: (bool, str) (boolean for dataset validity, str for reason of invalidity)

        """
        valid = False
        reason = "No openlog connection"
        if self.openlog_connection:
            valid = True
            datasets = [
                self.dataset_cb.itemText(i) for i in range(self.dataset_cb.count())
            ]
            if dataset in datasets:
                valid = False
                reason = f"Dataset name is already available"
        return valid, reason
