# -*- coding: utf-8 -*-
"""THU_SO_TAY.PY — canh sổ tay phiên + hồ sơ khách (23/09/2026).

Chủ dự án muốn tư vấn "mượt, không vỡ ngữ cảnh", và quyết định:
  - hồ sơ KHÔNG dùng dữ liệu mua hàng (bảng purchase),
  - bot dùng hồ sơ NGẦM, không nói "lần trước anh hỏi...",
  - giữ vĩnh viễn, tự phân loại + tóm tắt.

Bộ này canh năm tính chất, không cần CSDL, không gọi mạng:

  1. Hỏi nối tiếp không nêu sản phẩm vẫn biết đang bàn sản phẩm nào — kể
     cả khi lượt nêu sản phẩm đã trôi khỏi GIU_LICH_SU lượt gần nhất.
  2. Câu ngắn nối tiếp được ghép sản phẩm phiên để thử lại TẦNG 1.
  3. Khối hồ sơ đi vào prompt tầng 2, và prompt có luật DÙNG NGẦM.
  4. Key khách dán vào chat không lọt vào sổ tay, lịch sử lưu, hay prompt.
  5. `congcu.licence.key_cua_khach` và bảng purchase không hề được chạm
     tới khi dựng hồ sơ.

Chạy:  python thu/thu_so_tay.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)

logging.disable(logging.WARNING)
os.environ['CHATBOT_NHA_CUNG_CAP'] = 'rong'

from giao_uoc import NguCanh  # noqa: E402
import nao.tra_loi as tl  # noqa: E402
from llm.giao_dien import dung_prompt_dien_giai  # noqa: E402
from congcu import phien as ph  # noqa: E402

KEY_GIA = 'QX7M2K9P4L8N3V6B'
truot = 0


def kiem(dieu_kien: bool, ten: str) -> None:
    global truot
    print(('  [OK]    ' if dieu_kien else '  [TRƯỢT] ') + ten)
    if not dieu_kien:
        truot += 1


# Không có CSDL: ép các hàm CSDL về "không có gì" để chạy bằng RAM, và thay
# tai_ho_so bằng hồ sơ giả — đúng hình dạng congcu.phien.tai_ho_so trả về.
HO_SO_GIA = {
    'khoa': 'u:khach_thu', 'so_tay': {'nen_tang': 'MT4', 'san': ['Exness'], 'san_pham': ['dcabot']},
    'tom_tat': 'Khách dùng dcabot trên MT4 Exness, đang so giá gói tháng và gói năm.',
    'phan_loai': 'hoi_gia', 'muc_quan_tam': 'nong',
    'bang_khoan': 'sợ cháy tài khoản', 'luu_y': 'hướng dẫn theo MT4, nhấn mạnh quản lý vốn',
    'so_phien': 2,
}
tl.tai_phien = lambda phien: None
tl.luu_phien = lambda *a, **k: True
tl.nhan_ve_tai_khoan = lambda *a, **k: None
tl.tai_ho_so = lambda nd, may: HO_SO_GIA if nd == 'khach_thu' else None


def _no(*a, **k):
    raise AssertionError('key_cua_khach bị gọi khi dựng hồ sơ — cấm (quyết định 23/09/2026)')


tl.key_cua_khach = _no

print('=== 1) Hỏi nối tiếp: sản phẩm không trôi mất sau 3 lượt ===')
nc_phien = 'thu-so-tay-1'
# Chèn đủ lượt đệm để lượt nêu "copytrade" TRÔI KHỎI GIU_LICH_SU lượt gần
# nhất — con số đó đổi (3 -> 6 ngày 23/09/2026) thì phép thử tự theo.
from giao_uoc import GIU_LICH_SU  # noqa: E402
_dem = tuple(f'ok {i}' for i in range(GIU_LICH_SU))
for cau in ('cài copytrade thế nào', 'em dùng mt5 sàn vantage') + _dem + ('thế bước 2 làm sao',):
    nc = NguCanh(phien=nc_phien, ngon_ngu='vi', nguoi_dung='khach')
    tl.tra_loi(cau, nc)
kiem(nc.so_tay.get('dang_hoi') == 'copytrade', f"sổ tay vẫn nhớ sản phẩm = {nc.so_tay.get('dang_hoi')!r}")
kiem(nc.so_tay.get('nen_tang') == 'MT5' and nc.so_tay.get('san') == ['Vantage'], 'sổ tay nhớ MT5 + Vantage')
kiem(not any('copytrade' in (l.get('noi_dung') or '') for l in nc.lich_su if l['vai'] == 'khach'),
     'lượt nêu "copytrade" đã trôi khỏi GIU_LICH_SU lượt gần nhất (điều kiện của phép thử)')
kiem(tl._da_biet_san_pham('thế bước 2 làm sao', nc), 'không hỏi lại sản phẩm dù lịch sử gần không còn nó')

print('\n=== 2) Câu ngắn được ghép sản phẩm để thử lại tầng 1 ===')
kiem(tl._cau_kem_san_pham('còn mt4 thì sao', nc) == 'còn mt4 thì sao copytrade', 'ghép "copytrade"')
kiem(tl._cau_kem_san_pham('dcabot cài sao', nc) is None, 'câu đã nêu sản phẩm thì KHÔNG ghép')
kiem(tl._cau_kem_san_pham('một câu hỏi rất dài có tới hơn tám chữ ở trong này nhé', nc) is None,
     'câu dài thì KHÔNG ghép')

print('\n=== 3) Hồ sơ đi vào prompt tầng 2, kèm luật DÙNG NGẦM ===')
nc3 = NguCanh(phien='thu-so-tay-3', ngon_ngu='vi', nguoi_dung='khach_thu')
tl._cap_nhat_so_tay_luot(nc3, 'bot có an toàn không', 'khac')
nc3.ho_so = tl._dung_khoi_ho_so(nc3)
kiem('MT4' in nc3.ho_so and 'Exness' in nc3.ho_so, 'lấy nền tảng/sàn từ hồ sơ khi phiên chưa nêu')
kiem('sợ cháy tài khoản' in nc3.ho_so and 'quản lý vốn' in nc3.ho_so, 'có băn khoăn + lưu ý')
kiem(len(nc3.ho_so) <= 2500, f'khối hồ sơ bị giới hạn độ dài ({len(nc3.ho_so)} ký tự)')
he_thong, nguoi_dung = dung_prompt_dien_giai('bot có an toàn không', [], nc3)
kiem('DÙNG NGẦM' in he_thong and 'lần trước' in he_thong, 'câu dẫn hệ thống có luật DÙNG NGẦM')
kiem('Điều đã biết về khách' in nguoi_dung and 'sợ cháy tài khoản' in nguoi_dung, 'khối hồ sơ nằm trong prompt')
nc_khach = NguCanh(phien='thu-so-tay-3b', ngon_ngu='vi', nguoi_dung='khach')
_, nd_rong = dung_prompt_dien_giai('xin chào', [], nc_khach)
kiem('Điều đã biết về khách' not in nd_rong, 'khách chưa có gì thì KHÔNG chèn khối rỗng')

print('\n=== 4) Key dán vào chat không lọt đi đâu ===')
nc4 = NguCanh(phien='thu-so-tay-4', ngon_ngu='vi', nguoi_dung='khach')
ghi_lai = {}
tl.luu_phien = lambda phien, **k: ghi_lai.update(k) or True
tl.tra_loi(f'key {KEY_GIA} báo sai key trên mt5', nc4)
kiem(KEY_GIA not in repr(nc4.so_tay), 'không có trong sổ tay')
kiem(KEY_GIA not in repr(ghi_lai.get('lich_su')), 'không có trong lịch sử ghi xuống chat_phien')
kiem(KEY_GIA not in tl._dung_khoi_ho_so(nc4), 'không có trong khối hồ sơ gửi mô hình')
kiem('báo lỗi key/licence' in nc4.so_tay.get('loi', []), 'nhưng VẪN ghi nhận là khách gặp lỗi key')

print('\n=== 5) Phiên ẩn danh không có hồ sơ, không tạo khoá rỗng ===')
kiem(ph.khoa_ho_so('khach', '') is None, 'không may, không đăng nhập -> không có hồ sơ')
kiem(ph.khoa_ho_so('khach', 'abc') == 'm:abc', 'có mã thiết bị -> hồ sơ m:<may>')

print()
if truot:
    print(f'THU_SO_TAY: {truot} phép thử TRƯỢT.')
    sys.exit(1)
print('THU_SO_TAY: QUA HẾT.')
