# -*- coding: utf-8 -*-
"""CSDL.PY — một chỗ DUY NHẤT để mở kết nối MySQL cho các module MỚI.

VÌ SAO FILE NÀY TỒN TẠI DÙ `san_pham.py` ĐÃ CÓ LOGIC Y HỆT
------------------------------------------------------------
Đây là bản sao CÓ CHỦ Ý của phần "đọc .env + mở kết nối pymysql" đang chạy
trong `congcu/san_pham.py`. KHÔNG gộp hai nơi làm một ở bước này, vì
san_pham.py đang chạy sản xuất (phục vụ giá/danh mục thật cho khách) —
refactor nó để dùng chung với module mới là việc có ích nhưng có RỦI RO
không cần thiết ngay lúc này (lỡ tay đổi hành vi cache/độ trễ của một
đường đang chạy). Khi nào có thời gian kiểm thử kỹ, gộp lại là việc làm
sau, không phải bây giờ.

Vì vậy: nơi khác cần mở kết nối MySQL trong các module MỚI thì import từ
đây (`cau_hinh_db()`, `ket_noi()`), KHÔNG copy thêm một bản thứ ba.

Cách chạy tự kiểm (không cần CSDL thật):
    python congcu/csdl.py
"""
from __future__ import annotations

import logging
import os
from typing import Dict, Optional


# =====================================================================
# CẤU HÌNH KẾT NỐI — đọc từ .env của website, KHÔNG viết cứng mật khẩu
# =====================================================================

# Đường dẫn ĐẦU TIÊN suy ĐỘNG từ vị trí tệp này, để repo đặt ở đâu cũng
# chạy: <goc>/private/services/chatbot/congcu/csdl.py -> lên 3 cấp cha
# (congcu -> chatbot -> services) là <goc>/private, rồi ghép '.env'.
_THU_MUC_CONGCU = os.path.dirname(os.path.abspath(__file__))
_THU_MUC_PRIVATE_SUY_DONG = os.path.dirname(os.path.dirname(os.path.dirname(_THU_MUC_CONGCU)))

# Hai đường dẫn còn lại GIỮ NGUYÊN như san_pham.py đang dùng, để hành vi
# không đổi trên hai máy đã biết (local Windows/XAMPP, máy chủ thật) kể cả
# khi vì lý do gì đó suy đoán động ở trên trật đường.
DUONG_DAN_ENV_UNG_VIEN = (
    os.path.join(_THU_MUC_PRIVATE_SUY_DONG, '.env'),
    '/home/admin/web/tradingauto.org/private/.env',
)


def _doc_tep_env(duong_dan: str) -> Dict[str, str]:
    """Đọc thô một tệp .env kiểu KHOA=gia_tri. Không dùng thư viện ngoài
    để tránh phụ thuộc thêm — cú pháp .env ở đây đơn giản, tự phân tích
    là đủ. (Y hệt logic trong san_pham.py, xem chú thích đầu tệp.)"""
    ket_qua: Dict[str, str] = {}
    try:
        with open(duong_dan, encoding='utf-8') as tep:
            for dong in tep:
                dong = dong.strip()
                if not dong or dong.startswith('#') or '=' not in dong:
                    continue
                khoa, _, gia_tri = dong.partition('=')
                ket_qua[khoa.strip()] = gia_tri.strip().strip('"').strip("'")
    except OSError:
        # Không có tệp ở đường dẫn này — bình thường, thử đường dẫn kế tiếp.
        return {}
    return ket_qua


def cau_hinh_db() -> Optional[Dict[str, str]]:
    """Gộp cấu hình: biến môi trường ĐÈ LÊN giá trị đọc từ .env, vì máy
    chủ thật thường set biến môi trường trực tiếp (an toàn hơn tệp).

    Trả None nếu thiếu DB_HOST/DB_NAME/DB_USER — KHÔNG ném lỗi.
    """
    tep_env: Dict[str, str] = {}
    for duong_dan in DUONG_DAN_ENV_UNG_VIEN:
        tep_env = _doc_tep_env(duong_dan)
        if tep_env:
            break

    def lay(khoa: str) -> Optional[str]:
        return os.environ.get(khoa) or tep_env.get(khoa)

    host, ten_db, user = lay('DB_HOST'), lay('DB_NAME'), lay('DB_USER')
    mat_khau = lay('DB_PASS') or ''
    if not (host and ten_db and user):
        logging.warning(
            'Không tìm thấy cấu hình CSDL (thiếu DB_HOST/DB_NAME/DB_USER trong '
            'biến môi trường hoặc tệp .env ở các đường dẫn ứng viên).'
        )
        return None
    return {'host': host, 'name': ten_db, 'user': user, 'pass': mat_khau}


def ket_noi():
    """Mở một kết nối pymysql MỚI, hoặc None nếu thiếu cấu hình / chưa cài
    pymysql / không nối được. KHÔNG BAO GIỜ ném lỗi ra ngoài — mọi nhánh
    lỗi chỉ ghi logging.warning tiếng Việt rồi trả None.

    Không tự đóng giùm: nơi gọi tự chịu trách nhiệm dùng `with ket_noi() as
    conn:` (pymysql Connection hỗ trợ context manager) hoặc tự gọi .close().
    """
    try:
        import pymysql  # import trễ: máy không cài thư viện này vẫn chạy được các phần khác
    except ImportError:
        logging.warning(
            'Chưa cài thư viện pymysql nên không mở được kết nối CSDL. '
            'Cài bằng lệnh: pip install pymysql'
        )
        return None

    cau_hinh = cau_hinh_db()
    if not cau_hinh:
        # cau_hinh_db() đã tự ghi log lý do, không lặp lại ở đây.
        return None

    try:
        return pymysql.connect(
            host=cau_hinh['host'],
            user=cau_hinh['user'],
            password=cau_hinh['pass'],
            database=cau_hinh['name'],
            charset='utf8mb4',
            cursorclass=pymysql.cursors.DictCursor,
            connect_timeout=5,
        )
    except Exception as loi:  # noqa: BLE001 - cố tình bắt rộng, đây là biên mạng
        # KHÔNG in cau_hinh['pass'] hay bất cứ gì có thể lẫn mật khẩu.
        logging.warning('Không kết nối được CSDL (host=%s, db=%s): %s',
                         cau_hinh['host'], cau_hinh['name'], loi)
        return None


def voi_ct(lay, viec):
    """Mở kết nối NGẮN (`lay()`, mặc định ket_noi), làm `viec(ct)`, commit, đóng; trả kết quả của `viec`.

    MySQL máy chủ ngắt kết nối rảnh sau 10 GIÂY (wait_timeout). Việc nào phải chờ mạng lâu (gọi mô hình,
    Facebook, Telegram gửi ảnh…) thì làm GIỮA hai lần voi_ct, không bao giờ giữ một kết nối qua chỗ chờ đó.
    02/10/2026: chăm sóc khách giữ kết nối suốt lúc AI soạn (13–19 giây) -> gửi xong ghi sổ lỗi -> một khách
    nhận 3 tin. Ném RuntimeError nếu không mở được kết nối."""
    c = (lay or ket_noi)()
    if c is None:
        raise RuntimeError('Không kết nối được CSDL.')
    with c:
        with c.cursor() as ct:
            kq = viec(ct)
        c.commit()
    return kq


# =====================================================================
# TỰ KIỂM — chạy được kể cả khi không có CSDL
# =====================================================================
if __name__ == '__main__':
    logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')

    print('=== Tự kiểm csdl.py ===')
    print()
    print('Đường dẫn .env ứng viên (theo thứ tự thử):')
    for dd in DUONG_DAN_ENV_UNG_VIEN:
        print(f'  - {dd}  {"(tồn tại)" if os.path.isfile(dd) else "(không có)"}')
    print()

    cau_hinh = cau_hinh_db()
    if cau_hinh:
        print(f'Tìm thấy cấu hình CSDL: host={cau_hinh["host"]} db={cau_hinh["name"]} '
              f'user={cau_hinh["user"]} (mật khẩu KHÔNG in ra)')
    else:
        print('Không tìm thấy cấu hình CSDL — ĐÚNG như kỳ vọng nếu máy này chưa có .env.')

    print()
    conn = ket_noi()
    if conn is None:
        print('ket_noi() -> None (không nối được / thiếu cấu hình / thiếu pymysql). '
              'Chương trình KHÔNG bị sập — đúng như yêu cầu.')
    else:
        print('ket_noi() -> đã nối được CSDL thật.')
        try:
            conn.close()
        except Exception:  # noqa: BLE001
            pass
