from qgis.PyQt.QtWidgets import (
    QHeaderView,
    QLabel,
    QMessageBox,
    QTreeWidget,
    QTreeWidgetItem,
    QVBoxLayout,
    QWidget,
    QWizard,
    QWizardPage,
)

from openlog.datamodel.connection.interfaces.assay_interface import AssayInterface
from openlog.datamodel.connection.openlog_connection import OpenLogConnection
from openlog.gui.import_assay.wizard.assay_database_definition import (
    AssayDatabaseDefinitionPageWizard,
)
from openlog.gui.import_assay.wizard.assay_import import (
    AssayImportPageWizard,
    ExtendedAssayDelimitedTextImportWidget,
)
from openlog.gui.import_assay.wizard.assay_selection import AssaySelectionPageWizard
from openlog.toolbelt import PlgTranslator


class ConfirmationPageWizard(QWizardPage):
    def __init__(
        self,
        parent: QWidget,
        openlog_connection: OpenLogConnection,
        assay_selection_wizard: AssaySelectionPageWizard,
        assay_database_definition_wizard: AssayDatabaseDefinitionPageWizard,
        assay_import_wizard: AssayImportPageWizard,
    ) -> None:
        """
        QWizardPage used to confirm assay imports and commit database session

        Args:
            parent : QWidget parent
            openlog_connection: OpenLogConnection used to import data
            assay_selection_wizard: AssaySelectionPageWizard
            assay_database_definition_wizard: AssayDatabaseDefinitionPageWizard
            assay_import_wizard: AssayImportPageWizard
        """
        super().__init__(parent)
        self._openlog_connection = openlog_connection
        self._assay_selection_wizard = assay_selection_wizard
        self._assay_database_definition_wizard = assay_database_definition_wizard
        self._assay_import_wizard = assay_import_wizard

        self.setTitle(self.tr("Import summary"))

        label = QLabel(
            self.tr("Here are the data that will be imported into openlog connection.")
        )
        label.setWordWrap(True)

        self.layout = QVBoxLayout()
        self.layout.addWidget(label)

        self.tree_widget = QTreeWidget()
        self.tree_widget.setHeaderHidden(True)
        self.tree_widget.header().setSectionResizeMode(
            QHeaderView.ResizeMode.ResizeToContents
        )
        self.layout.addWidget(self.tree_widget)

        self.setLayout(self.layout)

    def initializePage(self) -> None:
        """
        Initialize page before show.

        Update tree widget with imported data

        """
        self.tree_widget.clear()
        self.tree_widget.setColumnCount(2)

        self._add_assay_definition_tree_item()
        self._add_assay_database_definition()
        self._add_categories_definition()
        self._add_assay_import_tree_item()

        self.tree_widget.resizeColumnToContents(0)
        self.tree_widget.resizeColumnToContents(1)

    def _add_assay_definition_tree_item(self) -> None:
        """
        Add QTreeWidgetItem for assay definition

        """
        assay_definition = self._assay_import_wizard.get_assay_definition()
        assay_definition_tw = QTreeWidgetItem(self.tree_widget)
        assay_definition_tw.setText(0, self.tr("Downhole data"))

        self._add_tree_item(
            assay_definition_tw, self.tr("Variable"), assay_definition.variable
        )
        self._add_tree_item(
            assay_definition_tw, self.tr("Domain"), assay_definition.domain.value
        )
        self._add_tree_item(
            assay_definition_tw, self.tr("Extent"), assay_definition.data_extent.value
        )

        column_tw = self._add_tree_item(
            assay_definition_tw, self.tr("Columns"), str(len(assay_definition.columns))
        )
        for col, val in assay_definition.columns.items():
            self._add_tree_item(column_tw, col, val.series_type.value)

    def _add_categories_definition(self) -> None:
        created_categories = (
            self._assay_database_definition_wizard.get_created_categories()
        )
        if len(created_categories):
            category_tw = QTreeWidgetItem(self.tree_widget)
            category_tw.setText(0, self.tr("Category"))
            for category in created_categories:
                cat_tw = self._add_tree_item(category_tw, category.name, "")
                self._add_tree_item(cat_tw, self.tr("Table name"), category.table_name)
                self._add_tree_item(
                    cat_tw, self.tr("Name column"), category.name_column
                )
                self._add_tree_item(cat_tw, self.tr("Schema"), category.schema)

    def _add_assay_database_definition(self) -> None:
        """
        Add QTreeWidgetItem for assay database definition

        """
        assay_database_definition = (
            self._assay_database_definition_wizard.get_assay_database_definition()
        )
        assay_database_definition_tw = QTreeWidgetItem(self.tree_widget)
        assay_database_definition_tw.setText(0, self.tr("Database definition"))

        self._add_tree_item(
            assay_database_definition_tw,
            self.tr("Table"),
            assay_database_definition.table_name,
        )
        self._add_tree_item(
            assay_database_definition_tw,
            self.tr("Hole ID column"),
            assay_database_definition.hole_id_col,
        )
        self._add_tree_item(
            assay_database_definition_tw,
            self.tr("Dataset column"),
            assay_database_definition.dataset_col,
        )
        self._add_tree_item(
            assay_database_definition_tw,
            self.tr("X column"),
            assay_database_definition.x_col,
        )

        column_tw = self._add_tree_item(
            assay_database_definition_tw,
            self.tr("Columns"),
            str(len(assay_database_definition.y_col)),
        )
        for col, val in assay_database_definition.y_col.items():
            self._add_tree_item(column_tw, col, val)

        self._add_tree_item(
            assay_database_definition_tw,
            self.tr("X end column"),
            assay_database_definition.x_end_col,
        )
        self._add_tree_item(
            assay_database_definition_tw,
            self.tr("Schema"),
            assay_database_definition.schema,
        )

    def _add_assay_import_tree_item(self) -> None:
        """
        Add QTreeWidgetItem for assay imported data

        """
        assay_import_tw = QTreeWidgetItem(self.tree_widget)
        assay_import_tw.setText(0, self.tr("Imported data"))

        assays = self._assay_import_wizard.get_assays()
        self._add_tree_item(assay_import_tw, self.tr("Collar count"), str(len(assays)))
        data_count = 0
        for assay in assays:
            data_count += len(assay.import_x_values)
        self._add_tree_item(assay_import_tw, self.tr("Data count"), str(data_count))

        if isinstance(
            self._assay_import_wizard.dataset_edit,
            ExtendedAssayDelimitedTextImportWidget,
        ):
            self._add_tree_item(
                assay_import_tw,
                self.tr("Removed rows (thickness of 0)"),
                str(self._assay_import_wizard.dataset_edit.removed_rows),
            )

    @staticmethod
    def _add_tree_item(
        parent: QTreeWidgetItem, name: str, value: str
    ) -> QTreeWidgetItem:
        """
        Create a QTreeWidget item with name and value column

        Args:
            parent: parent QTreeWidgetItem
            name: name
            value: value

        Returns: created QTreeWidgetItem

        """
        item = QTreeWidgetItem(parent)
        item.setText(0, name)
        item.setText(1, value)
        return item

    def validatePage(self) -> bool:
        """
        Import data

        Returns: True if no import error occured, False otherwise

        """

        valid = True
        try:
            categories_iface = self._openlog_connection.get_categories_iface()
            if self._assay_selection_wizard.assay_creation_needed():
                # Add new assay definition and database definition
                assay_definition = self._assay_import_wizard.get_assay_definition()
                assay_database_definition = (
                    self._assay_database_definition_wizard.get_assay_database_definition()
                )

                self._openlog_connection.get_assay_iface().add_assay_table(
                    assay_definition, assay_database_definition
                )

                # Add new categories
                created_categories = (
                    self._assay_database_definition_wizard.get_created_categories()
                )
                categories_iface.import_categories_table(created_categories)

            # Add used categories values
            cat_values = self._assay_import_wizard.get_used_categories_values()
            for table_name, value in cat_values.items():
                cat = categories_iface.get_categories_table(table_name)
                categories_iface.import_categories_data(cat, value)

            self._openlog_connection.get_assay_iface().import_assay_data(
                self._assay_import_wizard.get_assays()
            )

            self._openlog_connection.commit()
        except AssayInterface.ImportException as exc:
            valid = False
            QMessageBox.warning(self, self.tr("Import exception"), str(exc))
            self._openlog_connection.rollback()
        return valid


class AssayImportWizard(QWizard):
    def rejected(self) -> None:
        """
        Rollback openlog connection if data import is rejected

        """
        self.openlog_connection.rollback()
        super(AssayImportWizard).rejected()

    def __init__(
        self, openlog_connection: OpenLogConnection, parent: QWidget = None
    ) -> None:
        """
        QWizard to import assay into openlog connection from .csv files.

        Args:
            openlog_connection: OpenLogConnection used to import data
            parent : QWidget parent
        """
        super().__init__(parent)
        self.openlog_connection = openlog_connection

        # translation
        self.tr = PlgTranslator().tr
        self.setWindowTitle(self.tr("Downhole data import"))

        self.assay_selection_wizard = AssaySelectionPageWizard(
            self, self.openlog_connection
        )
        self.addPage(self.assay_selection_wizard)

        self.assay_import_wizard = AssayImportPageWizard(
            self, self.openlog_connection, self.assay_selection_wizard
        )
        self.addPage(self.assay_import_wizard)

        self.assay_selection_wizard.assay_definition_widget.data_extent_cb.currentIndexChanged.connect(
            self.assay_import_wizard._set_import_options
        )

        self.assay_database_definition_wizard = AssayDatabaseDefinitionPageWizard(
            self,
            self.openlog_connection,
            self.assay_selection_wizard,
            self.assay_import_wizard,
        )
        self.addPage(self.assay_database_definition_wizard)

        self.confirmation_page = ConfirmationPageWizard(
            self,
            self.openlog_connection,
            self.assay_selection_wizard,
            self.assay_database_definition_wizard,
            self.assay_import_wizard,
        )

        # Wizard to confirm data import and commit database connection
        self.addPage(self.confirmation_page)
