"""
domain/db.py
============
MySQL connection factory for domain services.

Provides a SQLite-compatible _ConnectionProxy so existing
conn.execute(sql, params).fetchall() call sites work unchanged.
Translates ? → %s placeholders transparently.
DictCursor ensures row["column"] access matches the former sqlite3.Row pattern.
"""

from __future__ import annotations

import re
from contextlib import contextmanager
from typing import Generator

import pymysql
import pymysql.cursors

from domain.core.mysql_db import get_connection

_QMARK_RE = re.compile(r"\?")


class _CursorProxy:
    """Wraps a PyMySQL DictCursor to look like a sqlite3.Cursor."""

    __slots__ = ("_cur",)

    def __init__(self, cur: pymysql.cursors.DictCursor) -> None:
        self._cur = cur

    def fetchall(self) -> list[dict]:
        return self._cur.fetchall()

    def fetchone(self) -> dict | None:
        return self._cur.fetchone()

    @property
    def description(self):
        return self._cur.description

    @property
    def lastrowid(self) -> int | None:
        return self._cur.lastrowid

    def __iter__(self):
        return iter(self.fetchall())


class _ConnectionProxy:
    """
    Makes a PyMySQL connection behave like a sqlite3.Connection.

    - Translates ? → %s so call sites need no placeholder changes.
    - DictCursor returns dicts; row["column"] works exactly as with sqlite3.Row.
    - row_factory setter is a no-op (DictCursor already returns dicts).
    """

    __slots__ = ("_conn",)

    def __init__(self, conn) -> None:
        self._conn = conn

    def execute(self, sql: str, params=None) -> _CursorProxy:
        sql = _QMARK_RE.sub("%s", sql)
        cur = self._conn.cursor()
        cur.execute(sql, params or ())
        return _CursorProxy(cur)

    def executemany(self, sql: str, params_list) -> _CursorProxy:
        sql = _QMARK_RE.sub("%s", sql)
        cur = self._conn.cursor()
        cur.executemany(sql, params_list)
        return _CursorProxy(cur)

    def cursor(self) -> pymysql.cursors.DictCursor:
        return self._conn.cursor()

    def commit(self) -> None:
        self._conn.commit()

    def close(self) -> None:
        self._conn.close()

    # Ignore SQLite row_factory setter — DictCursor already returns dicts
    @property
    def row_factory(self):
        return None

    @row_factory.setter
    def row_factory(self, value) -> None:
        pass


@contextmanager
def get_db() -> Generator[_ConnectionProxy, None, None]:
    """Yield a MySQL _ConnectionProxy, closing on exit."""
    conn = get_connection()
    proxy = _ConnectionProxy(conn)
    try:
        yield proxy
    finally:
        conn.close()
