"""
Abstract base class for a connection pool.
"""

import inspect
import socket
from abc import ABC
from typing import Optional

from ecmind_blue_client.pool._server_connection_settings import ServerConnectionSettings
from ecmind_blue_client.rpc import (
    DEFAULT_CA_DATA,
    BlueException,
    JobParameter,
    JobParameterTypes,
    JobResult,
    RpcConnection,
    password_encrypt,
    password_reveal,
)


class AbstractPoolClient(ABC):
    """
    Abstract base class for a connection pool.

    Args:
        servers (list[ServerConnectionSettings] | str): List of server connection settings or a string representing the servers.
        name (str): The name of the client. This name is visible in the enterprise manager as application name.
        username (str): The username to authenticate with the server.
        password (str): The password to authenticate with the server.
        use_ssl (bool): Whether to use SSL for the connection.
        file_cache_byte_limit (int): The maximum size of the file cache in bytes.
        pool_size (int): The size of the connection pool.
        connect_timeout (int): The timeout for connecting to the server.
        pool_timeout (int): The timeout for getting a connection from the pool.
        cadata (str | None): The CA data to verify the server's SSL certificate.
    """

    def __init__(
        self,
        *,
        servers: list[ServerConnectionSettings] | str,
        username: str,
        password: str,
        name: Optional[str] = None,
        use_ssl: bool = True,
        file_cache_byte_limit: int = 33554432,
        pool_size: int = 10,
        connect_timeout: int = 10,
        pool_timeout: int = -1,
        cadata: str | None = None,
    ):

        self._username = username
        self._password = password
        self._name = name if name is not None else inspect.stack()[2].frame.f_globals.get("__name__", "__main__")

        self._use_ssl = use_ssl
        self._file_cache_byte_limit = file_cache_byte_limit

        self._connection_pool_size = pool_size
        self._connect_timeout = connect_timeout
        self._connection_pool_timout = pool_timeout

        self._cadata = cadata if cadata is not None else DEFAULT_CA_DATA

        # Parse servers if it's a connection string
        if isinstance(servers, str):
            servers = ServerConnectionSettings.from_string(servers)
        self._servers = servers

    _connections_in_use = 0

    @property
    def connections_in_use(self):
        """
        Returns the number of connection instances currently in use.

        Returns:
            int: The current number of connections in use.
        """
        return self._connections_in_use

    @staticmethod
    def _session_attach_prepare() -> list[JobParameter]:
        parameter = [
            JobParameter("Flags", 0, JobParameterTypes.INTEGER),
            JobParameter("SessionGUID", "", JobParameterTypes.STRING),
        ]

        return parameter

    @staticmethod
    def _session_attach_parse(connection: RpcConnection, result: JobResult) -> str:

        if result.return_code != 0:
            raise BlueException(result.return_code, f"krn.SessionAttach failed with result code {result.return_code}", result.errors)

        session_guid = result.get("SessionGUID", str)
        connection.session_guid = session_guid
        return session_guid

    @staticmethod
    def _session_properties_set_prepare(name: str) -> list[JobParameter]:
        parameter = [
            JobParameter("Flags", 0, JobParameterTypes.INTEGER),
            JobParameter("Properties", "instname;statname;address", JobParameterTypes.STRING),
            JobParameter("address", f"{socket.gethostbyname(socket.gethostname())}=dummy", JobParameterTypes.STRING),
            JobParameter("instname", name, JobParameterTypes.STRING),
            JobParameter("statname", socket.gethostname(), JobParameterTypes.STRING),
        ]

        return parameter

    @staticmethod
    def _session_properties_set_parse(result: JobResult) -> None:

        if result.return_code != 0:
            raise BlueException(result.return_code, f"krn.SessionPropertiesSet failed with result code {result.return_code}", result.errors)

    @staticmethod
    def _session_login_prepare(username: str, password: str) -> list[JobParameter]:
        parameter = [
            JobParameter("Flags", 0, JobParameterTypes.INTEGER),
            JobParameter("UserName", username, JobParameterTypes.STRING),
            JobParameter("UserPwd", password_encrypt(password_reveal(password)), JobParameterTypes.STRING),
        ]

        return parameter

    @staticmethod
    def _session_login_parse(connection: RpcConnection, result: JobResult, username: str) -> None:

        if result.return_code != 0:
            raise BlueException(result.return_code, f"krn.SessionLogin failed with result code {result.return_code}", result.errors)

        desciption = result.get("Description", str)
        action = result.get("Action", int)

        connection.user_guid = result.get("UserGUID", str)
        connection.user_id = result.get("UserID", int)
        connection.username = result.get("UserName", str, username)
        connection.pwd_expires = result.get("PwdExpires", int, -1)
        connection.unicode_system = result.get("UnicodeSystem", bool, False)

        if desciption != "":
            raise PermissionError(f"Login error ({action}): {desciption}")
