# -*- coding: utf-8 -*-
"""THU_KIN_KEY.PY — canh việc bot KHÔNG BAO GIỜ đọc key của khách ra chat.

Chủ dự án quyết định 23/09/2026, nguyên văn:
    "tắt phần tra key, tốt nhất ko nên lòi dữ liệu key của khách ra bảng
     tư vấn"

VÌ SAO CẦN MỘT BỘ TEST RIÊNG CHO VIỆC NÀY
------------------------------------------
Đây là loại yêu cầu rất dễ bị bật lại cho "tiện". Ai đó thấy nhánh tra key
trả về một câu chỉ đường thay vì tra thật, tưởng là chưa làm xong, rồi đấu
lại. Không có gì cản, và cũng không có gì báo — vì bot vẫn chạy bình thường,
chỉ là từ hôm đó mã key của khách bắt đầu nằm trong nhật ký hội thoại.

Bộ này canh HAI tính chất, và tính chất thứ hai mới là tính chất mạnh:

  1. Câu trả lời không chứa mã key / ngày hết hạn / mã sản phẩm.
  2. `congcu.licence.key_cua_khach` KHÔNG HỀ ĐƯỢC GỌI. Kiểm bằng cách thay
     nó bằng một hàm nổ tung — gọi tới là bộ test trượt ngay.

Tính chất 2 mạnh hơn vì nó chặn cả trường hợp ai đó tra key rồi vô tình để
dữ liệu rơi vào log, vào ngữ cảnh mô hình, hay vào bảng `chat_hoi_thoai` —
những chỗ mà kiểm chuỗi trong câu trả lời không nhìn thấy.

Chạy:  python thu/thu_kin_key.py
"""
from __future__ import annotations

import logging
import os
import sys

_THU_MUC_CHA = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if _THU_MUC_CHA not in sys.path:
    sys.path.insert(0, _THU_MUC_CHA)

# Dữ liệu GIẢ nhưng đúng hình dạng thật — nếu một chuỗi nào trong đây lọt ra
# câu trả lời thì đó chính là thứ sẽ lọt ra với khách thật.
MA_KEY_GIA = 'ZN2G9T66YW'
NGAY_GIA = '2026-12-31'
MA_SP_GIA = 'dcabot'

CAC_CAU_HOI = (
    'key của tôi còn hạn không',
    'cho tôi xem key',
    'license của em hết hạn chưa',
    'key dcabot của tôi là gì',
    'tôi còn mấy key',
    'check key giúp em',
)


def chay() -> int:
    """Trả về số ca SAI. 0 nghĩa là đạt."""
    from giao_uoc import KeyCuaKhach, NguCanh

    # --- Chốt 2: thay hàm tra key bằng một quả mìn ---------------------
    da_goi = {'co': False}

    def _min(*a, **k):
        da_goi['co'] = True
        raise AssertionError('congcu.licence.key_cua_khach() KHÔNG được phép gọi nữa')

    import congcu.licence as licence
    licence.key_cua_khach = _min
    import nao.tra_loi as tl
    tl.key_cua_khach = _min          # tra_loi.py giữ tham chiếu riêng lúc import

    # --- Chốt 3: câu hỏi key KHÔNG được rơi vào hàng đợi câu thiếu ------
    #
    # Đo thật trên 73 hội thoại đầu tiên: 57 câu là hỏi key, tức 78% lưu
    # lượng. Bot trả lời ĐÚNG hết, nhưng vì không đi qua đường tìm kiếm
    # nên câu nào cũng bị ghi vào `chat_thieu`. Bảng "câu bot không trả
    # lời được" thành ra 7/18 mục đầu là câu key, gom 57/70 lượt — phần
    # thiếu THẬT bị chôn, mở bảng ra không đọc được gì.
    #
    # Bắt bằng cách thay hàm ghi hàng đợi bằng một cái đếm, thay vì đọc
    # CSDL: test phải chạy được khi không có CSDL (máy dev không có).
    da_ghi_hang_doi = []
    tl._hang_doi_ghi_nhan = (
        lambda cau, y_dinh, sp, tra_loi, kg: da_ghi_hang_doi.append((cau, y_dinh))
    )

    # --- Chốt 1: câu trả lời không được chứa dữ liệu key ---------------
    from congcu.khuon_mau import cau_key
    ds_gia = [KeyCuaKhach(license_key=MA_KEY_GIA, ma_san_pham=MA_SP_GIA,
                          nguon='web', het_han=NGAY_GIA, con_han=True)]

    print('=' * 80)
    print('BOT CÓ ĐỌC KEY CỦA KHÁCH RA CHAT KHÔNG')
    print('=' * 80)
    so_sai = 0

    # 1a. cau_key() dù được đưa key THẬT vẫn không in ra
    for ngon_ngu in ('vi', 'en'):
        ra = cau_key(ds_gia, ngon_ngu)
        lo = [x for x in (MA_KEY_GIA, NGAY_GIA, MA_SP_GIA) if x in ra]
        dat = not lo
        so_sai += 0 if dat else 1
        print('%-54s %s' % ('cau_key(có key thật, %s) không in ra' % ngon_ngu,
                            'đạt' if dat else '<== LỘ: %s' % lo))

    # 1b. chạy nguyên tra_loi() với khách ĐÃ đăng nhập
    for cau in CAC_CAU_HOI:
        nc = NguCanh(phien='kin-key', ngon_ngu='vi', nguoi_dung='khach_that_da_dang_nhap')
        try:
            kq = tl.tra_loi(cau, nc)
        except AssertionError as loi:
            so_sai += 1
            print('%-54s <== %s' % (cau[:52], loi))
            continue
        except Exception as loi:  # noqa: BLE001
            so_sai += 1
            print('%-54s <== LỖI BẤT NGỜ %r' % (cau[:52], loi))
            continue
        lo = [x for x in (MA_KEY_GIA, NGAY_GIA, MA_SP_GIA) if x in kq.tra_loi]
        co_chi_duong = '/orders' in kq.tra_loi or kq.chuyen_nguoi
        dat = (not lo) and co_chi_duong
        so_sai += 0 if dat else 1
        print('%-54s %s' % (cau[:52],
                            'đạt' if dat else ('<== LỘ %s' % lo if lo else '<== không chỉ đường /orders')))

    print('-' * 80)
    dat_2 = not da_goi['co']
    so_sai += 0 if dat_2 else 1
    print('%-54s %s' % ('congcu.licence.key_cua_khach() KHÔNG được gọi',
                        'đạt' if dat_2 else '<== ĐÃ BỊ GỌI'))

    lot_hang_doi = [c for c, y in da_ghi_hang_doi if y == 'tra_key']
    dat_3 = not lot_hang_doi
    so_sai += 0 if dat_3 else 1
    print('%-54s %s' % ('câu hỏi key KHÔNG vào hàng đợi câu thiếu',
                        'đạt' if dat_3
                        else '<== %d câu lọt: %r' % (len(lot_hang_doi), lot_hang_doi[:2])))
    print('=' * 80)
    print('SỐ CA SAI: %d' % so_sai)
    if so_sai:
        print('Nếu ca này trượt: mã key của khách đang đi vào khung chat và vào')
        print('bảng chat_hoi_thoai. Sửa ngay, đừng để qua đêm.')
    return so_sai


if __name__ == '__main__':
    logging.disable(logging.CRITICAL)
    sys.exit(1 if chay() else 0)
