"""
数据库连接

使用 psycopg2 直连 PostgreSQL，不引入 ORM。
连接参数从环境变量读取（经 config 模块加载）。

提供两类连接：
- get_connection()：zqyy_app 读写连接（用户/队列/调度等业务数据）
- get_etl_readonly_connection(site_id)：etl_feiqiu 只读连接（数据库查看器），
  自动设置 RLS site_id 隔离

当 DEV_TRACE_ENABLED=true 且存在活跃 TraceContext 时，
get_connection() 返回 TracedConnection 包装，自动记录 DB_CONN / DB_QUERY / DB_CONN_RELEASE span。
"""

import time

import psycopg2
from psycopg2.extensions import connection as PgConnection

from app.config import (
    APP_DB_NAME,
    DB_HOST,
    DB_PASSWORD,
    DB_PORT,
    DB_USER,
    ETL_DB_HOST,
    ETL_DB_NAME,
    ETL_DB_PASSWORD,
    ETL_DB_PORT,
    ETL_DB_USER,
)

# TCP keepalive 参数：防止长期运行的后台服务连接被网络设备/PostgreSQL 回收
_KEEPALIVE_KWARGS = {
    "keepalives": 1,
    "keepalives_idle": 60,       # 空闲 60 秒后开始探测
    "keepalives_interval": 10,   # 每 10 秒探测一次
    "keepalives_count": 3,       # 连续 3 次失败判定断开
}

# 连接重试参数：应对 PostgreSQL 瞬时不可用（Tailscale 网络抖动等）
_CONNECT_MAX_RETRIES = 3
_CONNECT_RETRY_DELAY = 1.0  # 秒


def _connect_with_retry(connect_fn, max_retries=_CONNECT_MAX_RETRIES):
    """带重试的数据库连接，应对服务端瞬时拒绝连接。"""
    last_exc = None
    for attempt in range(max_retries):
        try:
            return connect_fn()
        except psycopg2.OperationalError as e:
            last_exc = e
            if attempt < max_retries - 1:
                time.sleep(_CONNECT_RETRY_DELAY * (attempt + 1))
    raise last_exc


def get_connection() -> PgConnection:
    """
    获取 zqyy_app 数据库连接。

    调用方负责关闭连接（推荐配合 contextmanager 或 try/finally 使用）。
    当 trace 启用且有活跃 TraceContext 时，返回 TracedConnection 包装，
    自动记录 DB_CONN span（连接获取耗时）并拦截后续 SQL 执行。
    """
    # CHANGE 2026-03-22 | task 8.2 | 集成 trace db_wrapper，仅 trace 启用时包装
    from app.trace.config import get_trace_config
    from app.trace.context import SpanType, TraceSpan, get_current_trace

    config = get_trace_config()
    should_trace = config.enabled and get_current_trace() is not None

    start = time.perf_counter() if should_trace else 0.0

    conn = _connect_with_retry(lambda: psycopg2.connect(
        host=DB_HOST,
        port=DB_PORT,
        user=DB_USER,
        password=DB_PASSWORD,
        dbname=APP_DB_NAME,
        **_KEEPALIVE_KWARGS,
    ))

    if should_trace:
        from datetime import datetime
        from app.trace.db_wrapper import traced_connection

        elapsed_ms = (time.perf_counter() - start) * 1000
        ctx = get_current_trace()
        # ctx 不为 None（上面已检查）
        ctx.add_span(TraceSpan(
            span_type=SpanType.DB_CONN,
            module="app.database",
            function="get_connection",
            description_zh=f"获取数据库连接，耗时 {elapsed_ms:.1f}ms",
            description_en=f"Acquired database connection in {elapsed_ms:.1f}ms",
            params={},
            result_summary=f"{elapsed_ms:.1f}ms",
            duration_ms=elapsed_ms,
            timestamp=datetime.now().isoformat(),
        ))
        return traced_connection(conn)

    return conn


def get_etl_global_readonly_connection() -> PgConnection:
    """
    获取 ETL 数据库的全局只读连接（不设 RLS）。

    用于系统管理后台等不需要门店隔离的场景（如 ETL 状态监控）。
    """
    conn = _connect_with_retry(lambda: psycopg2.connect(
        host=ETL_DB_HOST,
        port=ETL_DB_PORT,
        user=ETL_DB_USER,
        password=ETL_DB_PASSWORD,
        dbname=ETL_DB_NAME,
        **_KEEPALIVE_KWARGS,
    ))
    try:
        conn.autocommit = False
        with conn.cursor() as cur:
            cur.execute("SET default_transaction_read_only = on")
        conn.commit()
    except Exception:
        conn.close()
        raise
    return conn


def get_etl_readonly_connection(site_id: int | str) -> PgConnection:
    """
    获取 ETL 数据库（etl_feiqiu）的只读连接。

    连接建立后自动执行：
    1. SET default_transaction_read_only = on  — 禁止写操作
    2. SET LOCAL app.current_site_id = '{site_id}'  — 启用 RLS 门店隔离

    调用方负责关闭连接。典型用法::

        conn = get_etl_readonly_connection(site_id)
        try:
            with conn.cursor() as cur:
                cur.execute("SELECT ...")
        finally:
            conn.close()
    """
    conn = _connect_with_retry(lambda: psycopg2.connect(
        host=ETL_DB_HOST,
        port=ETL_DB_PORT,
        user=ETL_DB_USER,
        password=ETL_DB_PASSWORD,
        dbname=ETL_DB_NAME,
        **_KEEPALIVE_KWARGS,
    ))
    try:
        conn.autocommit = False
        with conn.cursor() as cur:
            # 会话级只读：防止任何写操作
            cur.execute("SET default_transaction_read_only = on")
            # 事务级 RLS 隔离：设置当前门店 ID
            cur.execute(
                "SET LOCAL app.current_site_id = %s", (str(site_id),)
            )
        conn.commit()
    except Exception:
        conn.close()
        raise
    return conn


def get_etl_write_connection() -> PgConnection:
    """
    获取 ETL 数据库（etl_feiqiu）的可写连接。

    仅用于后端需要回写 ETL 汇总表的场景（如 task_generator 回写关系指数统计）。
    不设置 RLS 隔离，调用方需在 SQL 中显式指定 site_id。
    调用方负责关闭连接。
    """
    conn = _connect_with_retry(lambda: psycopg2.connect(
        host=ETL_DB_HOST,
        port=ETL_DB_PORT,
        user=ETL_DB_USER,
        password=ETL_DB_PASSWORD,
        dbname=ETL_DB_NAME,
        **_KEEPALIVE_KWARGS,
    ))
    conn.autocommit = False
    return conn
