"""
sqlite_mock.py — Postgres Connection Interceptor (SQLite Fallback Layer)

KIẾN TRÚC:
  - Module này giả lập các Psycopg2/Psycopg3 Connection/Cursor cho môi trường dev.
  - MockCursor.execute() là SYNCHRONOUS (dùng sqlite3 trực tiếp).
  - Lý do KHÔNG dùng aiosqlite ở đây: UltraDescriptionDB, DescFieldConfig và các
    class helper khác gọi cursor.execute() theo cách ĐỒNG BỘ (classmethods thường).
    Thay đổi sang async sẽ phá vỡ >24 method trong ultra_desc_db.py và nhiều hơn nữa.
  
  LUỒNG ĐÚng:
    - async API routes (FastAPI) dùng `sqlite_db.py` (aiosqlite) TRỰc TIẾP.
    - Các helper class (UltraDescriptionDB, v.v.) gọi qua `pool_wrapper.get_pooled_connection_compat()`
      → trả về MockConnection → MockCursor SYNC.
"""

import logging
import re
import sqlite3
from contextlib import contextmanager

logger = logging.getLogger(__name__)

# ---------------------------------------------------------------------------
# DB Path (trỏ đến cùng file với sqlite_db.py)
# ---------------------------------------------------------------------------
from common.constants import SQLITE_DB_PATH


def _get_sync_conn() -> sqlite3.Connection:
    """Trả về kết nối sqlite3 ĐỒNG BỘ. Dùng cho MockCursor."""
    conn = sqlite3.connect(SQLITE_DB_PATH)
    conn.row_factory = sqlite3.Row
    return conn


# ---------------------------------------------------------------------------
# SQL Dialect Translator: Postgres/StarRocks → SQLite
# ---------------------------------------------------------------------------

def translate_query(query: str) -> str:
    """
    Dịch câu SQL đặc thù của Postgres/StarRocks sang dạng mà SQLite hiểu được.
    """
    # 1. Thay thế Placeholder của Postgres (%s) thành của SQLite (?)
    q = re.sub(r'(?<!%)%s(?!%)', '?', query)

    # 2. Thay thế Postgres Schema (dashboard_canifa)
    q = re.sub(r'"dashboard_canifa"\."([a-zA-Z0-9_]+)"', r'pg__dashboard_canifa__\1', q)
    q = re.sub(r'dashboard_canifa\.([a-zA-Z0-9_]+)', r'pg__dashboard_canifa__\1', q)

    # Thay thế Postgres Schema (canifa_chat)
    q = re.sub(r'"canifa_chat"\."([a-zA-Z0-9_]+)"', r'pg__canifa_chat__\1', q)
    q = re.sub(r'canifa_chat\.([a-zA-Z0-9_]+)', r'pg__canifa_chat__\1', q)

    # Thay thế Postgres Schema (public)
    q = re.sub(r'"public"\."([a-zA-Z0-9_]+)"', r'pg__public__\1', q)
    q = re.sub(r'public\.([a-zA-Z0-9_]+)', r'pg__public__\1', q)

    # 3. Thay thế cấu trúc của StarRocks
    q = re.sub(
        r'([a-zA-Z0-9_]+\.)?`?magento_product_dimension_with_text_embedding`?',
        r'sr__test_db__magento_product_dimension_with_text_embedding',
        q
    )

    # 4. Vá các ngữ pháp (Dialect) bị lệch pha
    # ANY_VALUE(col) -> MAX(col)
    q = re.sub(r'ANY_VALUE\s*\(', r'MAX(', q, flags=re.IGNORECASE)
    # MAX_BY(col, score) -> MAX(col)
    q = re.sub(r'MAX_BY\s*\(\s*([^,]+)\s*,\s*[^)]+\)', r'MAX(\1)', q, flags=re.IGNORECASE)
    # IF(cond, true, false) -> IIF(cond, true, false)
    q = re.sub(r'\bIF\s*\(', r'IIF(', q, flags=re.IGNORECASE)

    # 5. Handle StarRocks vector similarity (dummy value)
    q = re.sub(
        r'approx_cosine_similarity\s*\(\s*vector\s*,\s*\[[^\]]+\]\s*\)',
        r'0.5',
        q, flags=re.IGNORECASE
    )

    # 6. Strip ORDER BY ... NULLS LAST
    q = re.sub(r'NULLS\s+LAST', '', q, flags=re.IGNORECASE)

    # 7. Strip PostgreSQL type casts (::jsonb, ::text, ::int, etc.)
    q = re.sub(r'::[a-zA-Z_]+(?:\[\])?', '', q)

    # 8. NOW() → CURRENT_TIMESTAMP
    q = re.sub(r'\bNOW\s*\(\s*\)', 'CURRENT_TIMESTAMP', q, flags=re.IGNORECASE)

    # 9. ILIKE → LIKE
    q = re.sub(r'\bILIKE\b', 'LIKE', q, flags=re.IGNORECASE)

    # 10. TRUE/FALSE literals → 1/0
    q = re.sub(r'\bTRUE\b', '1', q, flags=re.IGNORECASE)
    q = re.sub(r'\bFALSE\b', '0', q, flags=re.IGNORECASE)

    # 11. ON CONFLICT DO NOTHING (SQLite supports this, keep as-is)
    # 12. RETURNING id → SQLite không hỗ trợ RETURNING. Strip nó.
    q = re.sub(r'\s+RETURNING\s+\S+', '', q, flags=re.IGNORECASE)

    return q


# ---------------------------------------------------------------------------
# MockCursor — SYNCHRONOUS, dùng sqlite3 trực tiếp
# ---------------------------------------------------------------------------

class MockRow(tuple):
    """
    Giả lập psycopg2 Row / aiomysql DictCursor kết hợp.
    Hỗ trợ truy cập qua chỉ mục (row[0]) và qua tên cột (row['id']).
    """
    def __new__(cls, cols, values):
        return super().__new__(cls, values)
        
    def __init__(self, cols, values):
        self._dict = dict(zip(cols, values))
        
    def __getitem__(self, key):
        if isinstance(key, str):
            return self._dict[key]
        return super().__getitem__(key)
        
    def keys(self):
        return self._dict.keys()
        
    def get(self, key, default=None):
        return self._dict.get(key, default)

class MockCursor:
    """
    Giả lập Psycopg2/3 Cursor, chạy ĐỒNG BỘ trên SQLite.
    
    KHÔNG chuyển sang async để tránh phá vỡ:
      - UltraDescriptionDB (24+ sync classmethods)
      - DescFieldConfig (7+ sync classmethods)
      - auth/models.py, agent/sql_agent/persistence.py, v.v.
    """
    def __init__(self):
        self._rows: list = []
        self.rowcount: int = 0
        self.description: list = []
        self._last_insert_id: int | None = None

    def execute(self, query: str, params: tuple = None):
        """Chạy truy vấn SQL — ĐỒNG BỘ, sqlite3 trực tiếp."""
        if params is None:
            params = ()

        tq = translate_query(query)
        logger.debug("[SQLITE MOCK] Query: %.120s", tq)

        conn = _get_sync_conn()
        try:
            cur = conn.cursor()
            cur.execute(tq, params)

            is_select = tq.strip().upper().startswith("SELECT")
            is_insert = tq.strip().upper().startswith("INSERT")

            if is_select:
                fetched = cur.fetchall()
                if cur.description:
                    cols = [d[0] for d in cur.description]
                    self._rows = [MockRow(cols, row) for row in fetched]
                    self.description = [(d[0],) for d in cur.description]
                else:
                    self._rows = []
                self.rowcount = len(self._rows)
            else:
                conn.commit()
                self.rowcount = cur.rowcount
                if is_insert:
                    self._last_insert_id = cur.lastrowid
                    # Giả lập RETURNING id → trả về lastrowid dưới dạng hàng đầu tiên
                    self._rows = [MockRow(["id"], [cur.lastrowid])] if cur.lastrowid else []
                else:
                    self._rows = []
        except Exception as e:
            logger.error("[SQLITE MOCK] Error: %s | Query: %.200s", e, tq)
            self._rows = []
            self.rowcount = 0
        finally:
            conn.close()

    def executemany(self, query: str, params_list):
        """Chạy nhiều tham số — ĐỒNG BỘ."""
        tq = translate_query(query)
        conn = _get_sync_conn()
        try:
            cur = conn.cursor()
            cur.executemany(tq, params_list)
            conn.commit()
            self.rowcount = cur.rowcount
            self._rows = []
        except Exception as e:
            logger.error("[SQLITE MOCK] executemany error: %s", e)
        finally:
            conn.close()

    def fetchall(self) -> list:
        return self._rows

    def fetchone(self) -> dict | tuple | None:
        if self._rows:
            return self._rows[0]
        return None

    def close(self):
        pass

    # Hỗ trợ `with cursor:` (sync context manager)
    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()


# ---------------------------------------------------------------------------
# MockConnection
# ---------------------------------------------------------------------------

class MockConnection:
    """
    Giả lập psycopg2/3 Connection.
    Trả về MockCursor đồng bộ khi gọi .cursor().
    """
    def cursor(self) -> MockCursor:
        return MockCursor()

    def commit(self):
        pass  # Commit được thực hiện trong MockCursor.execute()

    def rollback(self):
        pass

    def close(self):
        pass

    @contextmanager
    def transaction(self):
        yield self


# ---------------------------------------------------------------------------
# Factory functions — dùng bởi db_pool.py và pool_wrapper.py
# ---------------------------------------------------------------------------

@contextmanager
def get_mock_pg_conn():
    """
    Context manager: mock kết nối PostgreSQL → SQLite.
    Dùng bởi db_pool.CanifaDbPool.get_conn() khi USE_LOCAL_SQLITE=true.
    """
    logger.info("🛡️ [SQLITE MOCK] Đánh chặn Postgres connection → SQLite local")
    yield MockConnection()


def get_mock_pg_conn_compat() -> MockConnection:
    """
    Trực tiếp trả về MockConnection (không context manager).
    Dùng bởi pool_wrapper.get_pooled_connection_compat() khi USE_LOCAL_SQLITE=true.
    """
    logger.info("🛡️ [SQLITE MOCK COMPAT] Đánh chặn Postgres connection → SQLite local")
    return MockConnection()
