from typing import Optional

from sqlalchemy import create_engine
from sqlalchemy.engine import URL
from sqlalchemy.orm import Session, sessionmaker

from openlog.datamodel.connection.bdgeo.bdgeo_assay_interface import BDGeoAssayInterface
from openlog.datamodel.connection.bdgeo.bdgeo_layers_interface import (
    BDGeoLayersInterface,
)
from openlog.datamodel.connection.bdgeo.bdgeo_read_interface import BDGeoReadInterface
from openlog.datamodel.connection.interfaces.assay_interface import AssayInterface
from openlog.datamodel.connection.interfaces.layers_interface import LayersInterface
from openlog.datamodel.connection.interfaces.read_interface import ReadInterface
from openlog.datamodel.connection.openlog_connection import (
    Connection,
    OpenLogConnection,
)
from openlog.datamodel.connection.postgres_utils import create_session
from openlog.gui.connection.bdgeo_connection_dialog import BDGeoConnectionDialog

BASE_SETTINGS_KEY = "/BDGeoConnection"


class BDGeoConnection(OpenLogConnection):
    """
    OpenLogConnection interface implementation for bdgeo connection.

    """

    def __init__(self, connection: Connection):
        """
        OpenLogConnection interface for bdgeo database

        Args:
            connection: connection params
        """
        super().__init__()
        self._connection = connection
        self.session, self.engine = create_session(self._connection)

        self._layers_iface = BDGeoLayersInterface(connection)
        self._read_iface = BDGeoReadInterface(self.session)
        self._assay_iface = BDGeoAssayInterface(self.session)

    def get_layers_iface(self) -> LayersInterface:
        """
        Returns LayersInterface for all layer related methods

        Returns: (LayersInterface)

        """
        return self._layers_iface

    def get_read_iface(self) -> ReadInterface:
        """
        Returns ReadInterface for all read related methods (person, dataset, collar, survey and liths)

        Returns:

        """
        return self._read_iface

    def get_assay_iface(self) -> AssayInterface:
        """
        Returns AssayInterface for all read/write assay related methods

        Returns:

        """
        return self._assay_iface

    def selected_collar_surveying_available(self) -> bool:
        """
        Return True if the collar surveying is available
        :return: A boolean value.
        """
        return False

    def get_mainwindow_title(self) -> str:
        """
        Returns string for QGIS mainwindow title definition for connection
        raises OpenLogConnection.InvalidInterface if not implemented

        """
        return self.tr("BDGeo : database : {0} / user : {1}").format(
            self._connection.database, self._connection.user
        )

    def save_to_qgis_project(self, base_settings_key: str) -> None:
        """
        Save connection to QGIS project

        """
        base_key = base_settings_key + BASE_SETTINGS_KEY
        self._connection.save_to_qgis_project(base_key)

    @staticmethod
    def create_from_qgis_project(
        base_settings_key: str, parent
    ) -> Optional[OpenLogConnection]:
        """
        Create connection from QGIS project

        """
        base_key = base_settings_key + BASE_SETTINGS_KEY
        connection = Connection.from_qgis_project(base_key)
        widget = BDGeoConnectionDialog(parent)
        widget.set_connection_model(connection)
        if widget.exec():
            connection = widget.get_connection_model()
            openlog_connection = BDGeoConnection(connection)
            openlog_connection._copy_filter_from_project_layers(
                openlog_connection.get_layers_iface().get_collar_layer()
            )
            openlog_connection._copy_filter_from_project_layers(
                openlog_connection.get_layers_iface().get_collar_trace_layer()
            )
            return openlog_connection
        else:
            return None
