"""Namespace class for ADO database query operations (synchronous)."""

from __future__ import annotations

from typing import TYPE_CHECKING

from ecmind_blue_client.ecm._ecm_ado_parser import ECMAdoParser, ECMDbResult
from ecmind_blue_client.ecm._ecm_sql_params import bind_sql_params
from ecmind_blue_client.rpc._jobs import Jobs

if TYPE_CHECKING:
    from ecmind_blue_client.ecm.synchron._ecm_sync import ECMSync


class ECMDbSync:
    """Synchronous namespace for direct SQL queries via ``ado.ExecuteSQL``.

    Accessed via ``ecm.db`` on an :class:`ECMSync` instance.  Executes SQL
    against the ADO engine of the connected Blue server and returns typed
    result objects.

    Note:
        The connecting user requires appropriate database access rights on the
        server.  Data manipulation statements (INSERT, UPDATE, DELETE) must be
        explicitly enabled via a registry setting on the application server.
    """

    def __init__(self, ecm: "ECMSync") -> None:
        """Initialise the db namespace with a reference to the owning ECMSync instance.

        Args:
            ecm: The :class:`ECMSync` instance that owns this namespace.
        """
        self._ecm = ecm

    def select(self, sql: str, *params: int | str | float, flags: int = 0) -> ECMDbResult:
        """Execute a SQL SELECT statement and return the parsed result.

        Sends the query to the server via ``ado.ExecuteSQL`` with
        ``CursorType=-1`` (server registry default) and returns the parsed
        ADO Recordset XML as an :class:`ECMDbResult`.

        Positional *params* are substituted into *sql* via :func:`bind_sql_params`
        before the query is sent.  Use placeholders instead of string formatting
        to prevent SQL injection:

        - ``%s`` — quoted string
        - ``%w`` — quoted word (no whitespace or comment markers allowed)
        - ``%d`` — integer
        - ``%f`` — float
        - ``%u`` — unquoted identifier (table / column name)
        - ``%%`` — literal ``%``

        Args:
            sql: SQL template with optional placeholders.
            *params: Values substituted into the placeholders, in order.
            flags: Bitmask passed as the ``Flags`` parameter to ``ado.ExecuteSQL``.
                ``0`` = automatic ``N``-prefix for string literals on Unicode systems
                (default).  ``2`` = disable automatic ``N``-prefixing.

        Returns:
            An :class:`ECMDbResult` containing column definitions and result rows.

        Example::

            result = ecm.db.select(
                "SELECT id, benutzer FROM benutzer WHERE benutzer = %s",
                username,
            )
            for row in result:
                print(row["benutzer"])
        """
        bound = bind_sql_params(sql, *params)
        job_result = self._ecm.execute(Jobs.ADO_EXECUTESQL, Flags=flags, CursorType=-1, Command=bound)
        return ECMAdoParser(job_result.files[0].bytes()).parse()
