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

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 PersonSelectionWidget(QWidget):
    def __init__(self, parent: QWidget = None):
        """
        QWidget to display available person in OpenLogConnection and import person

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

        self.openlog_connection = None
        self.added_persons = []

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

        # Add button for person creation
        layout.addWidget(self.person_cb)
        self.create_button = QToolButton(self)
        self.create_button.setIcon(QgsApplication.getThemeIcon("mActionAdd.svg"))
        self.create_button.clicked.connect(self._create_person)
        layout.addWidget(self.create_button)
        self.setLayout(layout)

        self.create_button.setEnabled(False)
        self.person_cb.currentTextChanged.connect(
            lambda text: self.person_changed.emit(text)
        )

    # Signal emitted when person is changed.
    person_changed = QtCore.pyqtSignal(str)

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

        Returns: QComboBox

        """
        return self.person_cb

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

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

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

        OpenLogConnection.ImportData exception can be raised.

        """
        if self.openlog_connection:
            persons = [Person(person) for person in self.added_persons]
            try:
                self.openlog_connection.get_write_iface().import_persons(persons)
            except WriteInterface.ImportException as exc:
                QMessageBox.warning(self, "Import exception", str(exc))

    def refresh_person_code(self) -> None:
        """
        Refresh available person code from openlog connection

        """
        self.person_cb.clear()
        if self.openlog_connection:
            # Define person list from OpenLogConnection
            try:
                self.person_cb.addItems(
                    self.openlog_connection.get_read_iface().get_available_person_codes()
                )
            except ReadInterface.ReadException as exc:
                QMessageBox.warning(self, "Invalid database", str(exc))

    def selected_person_code(self) -> str:
        """
        Returns selected person

        Returns: selected person

        """
        return self.person_cb.currentText()

    def _create_person(self) -> None:
        """
        Create person from a QInputDialog definition

        """
        if self.openlog_connection:
            ok = True
            while ok:
                person, ok = QInputDialog.getText(self, "New person", "Person code")
                if person:
                    valid, reason = self._valid_person_code(person)
                    if valid:
                        self.person_cb.addItem(person)
                        self.person_cb.setCurrentText(person)
                        self.added_persons.append(person)
                        break
                    else:
                        QMessageBox.warning(self, "Invalid person code", reason)

    def _valid_person_code(self, person: str) -> (bool, str):
        """
        Check if person code is valid

        Args:
            person: person code

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

        """
        valid = False
        reason = "No openlog connection"
        if self.openlog_connection:
            person_code_max_size = (
                self.openlog_connection.get_write_iface().person_code_max_size()
            )
            valid = True
            persons = [
                self.person_cb.itemText(i) for i in range(self.person_cb.count())
            ]
            if len(person) > person_code_max_size:
                valid = False
                reason = f"Person code is limited to {person_code_max_size}"
            elif person in persons:
                valid = False
                reason = f"Person code is already available"
        return valid, reason
