from __future__ import annotations

import logging
import threading

from backends.explorer.discovery.connection_info import (
    AbstractConnectionInfo,
    KuzuConnectionInfo,
    Neo4jConnectionInfo,
)
from graph.connections.db_handler import DbHandler
from graph.connections.kuzu import KuzuLocalDbHandler, KuzuLocalReplicaDbHandler
from graph.connections.neo4j import Neo4DbHandler
from graph.connections.neo4j.preset import Neo4jPresetConfig

EXPLORER_KUZU_DB_DOWNLOAD_LOCK = threading.Lock()

logger = logging.getLogger(__name__)


class DbHandlerFactory:
    """Factory class for creating explorer database handlers."""

    @staticmethod
    def get_db_handler(
        db_info: AbstractConnectionInfo,
        neo4j_configuration: Neo4jPresetConfig | None = None,
    ) -> DbHandler:
        if isinstance(db_info, Neo4jConnectionInfo):
            if neo4j_configuration is None:
                raise ValueError("neo4j_configuration must be provided for Neo4jConnectionInfo")
            return DbHandlerFactory.get_neo4j_db_handler(db_info, neo4j_configuration)
        if isinstance(db_info, KuzuConnectionInfo):
            return DbHandlerFactory.get_kuzu_db_handler(db_info)
        raise ValueError(f"Unsupported db_info type: {type(db_info)}")

    @staticmethod
    def get_kuzu_db_handler(db_info: KuzuConnectionInfo) -> DbHandler:
        db_path_in_db_folder = db_info.get_db_path()

        logger.info(f"Retrieve db from path : {db_path_in_db_folder}")

        if db_info.is_remote:
            # Guard against the following situtation that happens only the first time the graph is visited:
            # 1. several concurrent requests to a graph without a replica downloaded yet
            # 2. the first request will trigger the download
            # 3. the subsequent ones used to use a malformed or non existing db, leading to empty results
            # To cover for this situation in a multi-threaded environment, we use a lock so the subsequent requests wait for the download to be complete.
            # This hack(-ish) fix will not work in a multi process environment though.
            # That's ok given the timeline we have to release the v1.0.0 of Visual Graph.
            with EXPLORER_KUZU_DB_DOWNLOAD_LOCK:
                return KuzuLocalReplicaDbHandler(db_path=db_path_in_db_folder, db_folder=db_info.db_folder)
        return KuzuLocalDbHandler(db_path=db_path_in_db_folder)

    @staticmethod
    def get_neo4j_db_handler(db_info: Neo4jConnectionInfo, neo4j_configuration: Neo4jPresetConfig) -> DbHandler:
        return Neo4DbHandler(
            uri=neo4j_configuration.neo4j_uri,
            username=neo4j_configuration.neo4j_username,
            password=neo4j_configuration.get_neo4j_password(),
            database=db_info.database_name,
        )
