import sys; sys.path.append("..")
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import QueuePool
import config.MysqlConfig as DatabaseConfig
from sshtunnel import SSHTunnelForwarder

# This class manages the MySQL connection, optionally establishes an SSH tunnel, and exposes a ready-to-use SQLAlchemy session.
class MysqlManager:

    # The constructor loads configuration, conditionally creates an SSH tunnel, builds an SQLAlchemy engine with a queue pool, and instantiates the first session.
    def __init__(self):

        # The variable “dbConfig” stores database connection parameters that are loaded from the configuration module.
        self.dbConfig = {
            "host": DatabaseConfig.MYSQL_HOST,
            "user": DatabaseConfig.MYSQL_USER,
            "password": DatabaseConfig.MYSQL_PASSWORD,
            "database": DatabaseConfig.MYSQL_DATABASE,
            "port": DatabaseConfig.MYSQL_PORT,
        }

        # This conditional branch builds an SSH tunnel when the configuration flag USE_SSH_TUNNEL is True; otherwise, it uses the direct database host and port.
        if getattr(DatabaseConfig, "USE_SSH_TUNNEL", False):

            # The variable “sshTunnel” opens a forwarder that connects the local port to the remote MySQL host through SSH.
            self.sshTunnel = SSHTunnelForwarder(
                (DatabaseConfig.SSH_HOST, DatabaseConfig.SSH_PORT),
                ssh_username=DatabaseConfig.SSH_USERNAME,
                ssh_password=DatabaseConfig.SSH_PASSWORD,
                remote_bind_address=(self.dbConfig["host"], self.dbConfig["port"]),
            )
            self.sshTunnel.start()

            # The variables “db_host” and “db_port” point to the locally forwarded endpoint when the tunnel is active.
            db_host = "127.0.0.1"
            db_port = self.sshTunnel.local_bind_port
        else:

            # The variable “sshTunnel” is set to None when no tunnel is required.
            self.sshTunnel = None

            # The variables “db_host” and “db_port” point to the remote MySQL server when no tunnel is used.
            db_host = self.dbConfig["host"]
            db_port = self.dbConfig["port"]

        # The variable “engine” creates an SQLAlchemy engine that uses a QueuePool and applies timeout settings taken from the configuration.
        self.engine = create_engine(
            f"mysql+pymysql://{self.dbConfig['user']}:{self.dbConfig['password']}@{db_host}:{db_port}/{self.dbConfig['database']}",
            echo=False,
            poolclass=QueuePool,
            pool_size=getattr(DatabaseConfig, "POOL_SIZE", 1),
            max_overflow=getattr(DatabaseConfig, "MAX_OVERFLOW", 0),
            pool_recycle=getattr(DatabaseConfig, "POOL_RECYCLE", 3600),
            pool_pre_ping=True,
            connect_args={"connect_timeout": getattr(DatabaseConfig, "CONNECT_TIMEOUT", 30)},
        )

        # The variable “dbSession” stores the first session instance created from the session factory bound to the engine.
        self.dbSession = sessionmaker(bind=self.engine)()

    # This method returns the current SQLAlchemy session so callers can interact with the database.
    def getSession(self):

        # The session instance is returned without creating a new one.
        return self.dbSession

    # This method closes the current session and shuts down the SSH tunnel when it was created.
    def close(self):

        # The session is closed to release database resources.
        self.dbSession.close()

        # This conditional branch stops the SSH tunnel when it exists.
        if self.sshTunnel:
            self.sshTunnel.stop()
