# -*- coding: utf-8 -*-
"""THU_NGUON.PY — canh phần "Nguồn tham khảo" gắn cuối câu trả lời.

Chủ dự án 23/09/2026 chụp một câu trả lời về Copytrade kèm ba nguồn:
    - Dùng CopyTrade — Tham số, Symbol Mapping, tỉ lệ lot: /sudung/copytrade
    - Câu hỏi thường gặp: /codebot/custom#cauhoi
    - 5. Nói rõ khi Master tăng lot: /update/copytrade#master-tang-lot
và nói: "câu trả lời cho khách bị trích dẫn thừa thãi". Chỉ dòng đầu đáng
đưa cho khách. Bộ này dựng lại ĐÚNG rổ đó và canh rằng chỉ dòng đầu còn lại.

Chạy:  python thu/thu_nguon.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)

from giao_uoc import Doan, KetQuaTim  # noqa: E402
from nao.tra_loi import _chon_nguon, NGUON_TOI_DA  # noqa: E402

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


def kq(duong_dan, san_pham, loai, diem, tieu_de='x'):
    return KetQuaTim(
        doan=Doan(ma=duong_dan, van_ban_vi='', van_ban_en='', san_pham=san_pham, loai=loai,
                  duong_dan=duong_dan, tieu_de=tieu_de, nguon_tep=''),
        diem=diem,
    )


print('=== 1) Đúng rổ chủ dự án chụp ===')
ro = [
    kq('/sudung/copytrade', 'copytrade', 'su_dung', 0.032, 'Dùng CopyTrade'),
    kq('/codebot/custom#cauhoi', None, 'dich_vu', 0.030, 'Câu hỏi thường gặp'),
    kq('/update/copytrade#master-tang-lot', 'copytrade', 'cap_nhat', 0.029, 'Nói rõ khi Master tăng lot'),
    kq('/sudung/copytrade#symbol-mapping', 'copytrade', 'su_dung', 0.028),
]
ra = [n['duong_dan'] for n in _chon_nguon(ro, 'copytrade')]
kiem(ra == ['/sudung/copytrade'], f'chỉ còn trang dùng copytrade: {ra}')

print('\n=== 2) Các luật lọc riêng lẻ ===')
ra = [n['duong_dan'] for n in _chon_nguon([
    kq('/guilde/dcabot#step-2', 'dcabot', 'huong_dan', 1.0),
    kq('/guilde/dcabot#step-4', 'dcabot', 'huong_dan', 0.9),
    kq('/sudung/dcabot', 'dcabot', 'su_dung', 0.8),
    kq('/faq/dcabot', 'dcabot', 'faq', 0.7),
], 'dcabot')]
kiem(ra == ['/guilde/dcabot#step-2', '/sudung/dcabot'], f'cùng trang khác neo lấy 1 lần, tối đa {NGUON_TOI_DA}: {ra}')

ra = [n['duong_dan'] for n in _chon_nguon([
    kq('/guilde/copytrade', 'copytrade', 'huong_dan', 1.0),
    kq('/sudung/copytrade', 'copytrade', 'su_dung', 0.3),
], 'copytrade')]
kiem(ra == ['/guilde/copytrade'], f'đoạn điểm thấp (< 60% đoạn tốt nhất) bị bỏ: {ra}')

ra = [n['duong_dan'] for n in _chon_nguon([
    kq('/update/copytrade#v2', 'copytrade', 'cap_nhat', 1.0),
    kq('/guilde/copytrade', 'copytrade', 'huong_dan', 0.9),
], 'copytrade')]
kiem(ra == ['/guilde/copytrade'], f'changelog không làm link, kể cả khi khớp nhất: {ra}')

print('\n=== 3) Đoán nguồn dự phòng (mô hình không khai) — thận trọng ===')
ra = _chon_nguon([kq('/faq', None, 'faq', 1.0)], 'copytrade')
kiem(ra == [], 'lọc hết thì KHÔNG gắn gì (không còn "giữ đoạn khớp nhất")')
kiem(_chon_nguon([], 'copytrade') == [], 'rổ rỗng -> không nguồn')
ra = [n['duong_dan'] for n in _chon_nguon([kq('/faq', None, 'faq', 1.0), kq('/pricing', None, 'faq', 0.9)], None)]
kiem(ra == ['/faq', '/pricing'], 'chưa biết sản phẩm thì đoạn dùng chung được trích')
ra = _chon_nguon([kq('/codebot/custom', 'codebot', 'dich_vu', 1.0),
                  kq('/sudung/signalbroadcaster', 'signalbroadcaster', 'su_dung', 0.9)], None)
kiem(ra == [], 'chưa biết sản phẩm, link thuộc 2 sản phẩm khác nhau -> không gắn (câu chung)')

print('\n=== 4) Mô hình KHAI nguồn đã dùng (23/09/2026) ===')
from nao.tra_loi import tach_khai_nguon, _nguon_theo_khai  # noqa: E402
van, so = tach_khai_nguon('Dạ, bật AutoTrading là chạy.\n\nNGUON_DA_DUNG: 2, 4')
kiem(so == [2, 4] and 'NGUON_DA_DUNG' not in van and van.endswith('là chạy.'), f'tách đúng: {so}')
van, so = tach_khai_nguon('Lỗi 4109 là chưa bật AutoTrading.\nNGUON_DA_DUNG: 0')
kiem(so == [] and 'NGUON' not in van, 'khai 0 -> không gắn nguồn nào (trả lời bằng kiến thức chung)')
van, so = tach_khai_nguon('Câu trả lời không khai gì.')
kiem(so is None, 'không khai -> None (lõi tự đoán nguồn như cũ)')
van, so = tach_khai_nguon('**NGUON_DA_DUNG**: 1\nThân bài.')
kiem(so == [1] and 'NGUON' not in van, 'khai sai chỗ / có định dạng vẫn bị xoá khỏi câu gửi khách')
ra = [n['duong_dan'] for n in _nguon_theo_khai(ro, [3, 1, 4, 2])]
kiem(ra == ['/sudung/copytrade', '/codebot/custom#cauhoi'],
     f'bỏ changelog khi có trang khác, mỗi trang một lần, tối đa {NGUON_TOI_DA}: {ra}')
ra = [n['duong_dan'] for n in _nguon_theo_khai(ro, [3])]
kiem(ra == ['/update/copytrade#master-tang-lot'], 'chỉ khai mỗi changelog thì vẫn gắn nó')
for cut in ('Thân bài.\nNGU', 'Thân bài.\n**NGUON_D', 'Thân bài.\nNGUON_'):
    van, _ = tach_khai_nguon(cut)
    kiem(van == 'Thân bài.', f'dòng khai bị CẮT DỞ {cut.splitlines()[-1]!r} không lộ ra khách')
van, _ = tach_khai_nguon('Dạ.\nNgu')
kiem(van.endswith('Ngu'), 'chữ thường "Ngu" (không phải nhãn) thì giữ nguyên')
kiem(_nguon_theo_khai(ro, [9]) == [], 'số đoạn ngoài phạm vi bị bỏ qua')
ro_chung = [kq('/codebot/custom', 'codebot', 'dich_vu', 1.0),
            kq('/sudung/signalbroadcaster#loc', 'signalbroadcaster', 'su_dung', 0.9)]
kiem(_nguon_theo_khai(ro_chung, [1, 2]) == [],
     'chưa biết sản phẩm, khai 2 sản phẩm khác nhau -> câu chung, không gắn link')
ra = [n['duong_dan'] for n in _nguon_theo_khai(ro_chung, [1, 2], 'signalbroadcaster')]
kiem(ra == ['/sudung/signalbroadcaster#loc'], f'đã biết sản phẩm -> bỏ đoạn sản phẩm khác: {ra}')

print('\n=== 5) Luồng đầy đủ với mô hình giả lập ===')
import nao.tra_loi as tl  # noqa: E402
from giao_uoc import NguCanh  # noqa: E402


class _MoHinhGia:
    ten = 'gia'
    cuoi = {}

    def dien_giai(self, cau_hoi, cac_doan, ngu_canh):
        self.cuoi = {'cac_doan': cac_doan}
        return 'Dạ, mã 4109 nghĩa là chưa bật AutoTrading.\nNGUON_DA_DUNG: 0'


class _BoTimRong:
    cac_doan = []

    def tim(self, *a, **k):
        return []


mh = _MoHinhGia()
tl._lay_nha_cung_cap_dung_chung = lambda: mh
tl.lay_bo_tim = lambda **k: _BoTimRong()
tl.tai_phien = lambda p: None
tl.luu_phien = lambda *a, **k: True
tl.tai_ho_so = lambda *a: None
tl._hang_doi_ghi_nhan = None
tl._nhat_ky_ghi = None
tl.khop = lambda cau, nc: None
kq = tl.tra_loi('copytrade báo lỗi 4109 là gì', NguCanh(phien='thu-nguon-1'))
kiem(not kq.chuyen_nguoi and 'AutoTrading' in kq.tra_loi,
     'không tìm được tài liệu -> VẪN tư vấn bằng kiến thức chung, không chuyển người')
kiem('NGUON_DA_DUNG' not in kq.tra_loi and kq.nguon == [], 'dòng khai bị xoá, không gắn nguồn')

muc_gia = {'id': 7, 'cau_hoi_chuan': 'master tăng lot thì slave có tăng theo không',
           'san_pham': 'copytrade', 'duong_dan_nguon': '/sudung/copytrade#ti-le-lot'}
tl.khop = lambda cau, nc: (muc_gia, 0.7)
tl.tra_loi_tu_muc = lambda muc, nc, d: tl.TraLoi(tra_loi='Slave tăng theo tỉ lệ.', tang=1, muc_id=7)
kq = tl.tra_loi('master đóng lệnh một phần thì slave copytrade có đóng theo không',
                NguCanh(phien='thu-nguon-2'))
dau = mh.cuoi['cac_doan'][0].doan if mh.cuoi.get('cac_doan') else None
kiem(kq.tang == 2 and dau is not None and dau.loai == 'tuvan',
     'khớp LỎNG (0.7) -> không trả nguyên văn, mục đi vào mô hình làm đoạn ưu tiên số 1')
tl.khop = lambda cau, nc: (muc_gia, 1.0)
kq = tl.tra_loi('master tăng lot thì slave có tăng theo không', NguCanh(phien='thu-nguon-3'))
kiem(kq.tang == 1 and kq.tra_loi == 'Slave tăng theo tỉ lệ.', 'khớp CHẮC (1.0) -> vẫn trả nguyên văn, 0 đồng')

print('\n=== 6) Câu hỏi GIÁ kèm nhiều việc khác ===')
_ghi_chu_thay = {}


def _dien_giai_ghi(cau_hoi, cac_doan, ngu_canh):
    _ghi_chu_thay['v'] = ngu_canh.ghi_chu
    return 'Chạy VPS được, cài theo 5 bước.\nNGUON_DA_DUNG: 0'


mh.dien_giai = _dien_giai_ghi
tl.khop = lambda cau, nc: None
tl._xu_ly_gia = lambda cau, nc, d: tl.TraLoi(tra_loi='BẢNG GIÁ TỪ CSDL', y_dinh='gia')
nc6 = NguCanh(phien='thu-nguon-6')
kq = tl.tra_loi('cho em hỏi dca bot giá sao, cài thế nào, chạy vps được không, '
                'với lại có copy sang tài khoản khác được không', nc6)
kiem(kq.tra_loi.startswith('BẢNG GIÁ TỪ CSDL') and 'Chạy VPS được' in kq.tra_loi,
     'bảng giá thật đặt trước, mô hình trả lời phần còn lại')
kiem('giá' in _ghi_chu_thay.get('v', '').lower() and nc6.ghi_chu == '',
     'mô hình được dặn đừng nhắc giá; lời dặn xoá sau lượt')
kq = tl.tra_loi('giá dca bot bao nhiêu', NguCanh(phien='thu-nguon-7'))
kiem(kq.tra_loi == 'BẢNG GIÁ TỪ CSDL' and not kq.da_goi_llm, 'câu hỏi giá gọn -> chỉ bảng giá, không gọi mô hình')

print('\n=== 7) Câu hỏi CHUNG CHUNG -> chỉ hỏi làm rõ, không nguồn ===')
# Đúng câu chủ dự án hỏi 23/09/2026 và bị đổ ra một bài trộn cài + dùng.
_seen = {}


class _BoTimCoDoan:
    cac_doan = []

    def tim(self, *a, **k):
        return [kq('/guilde/tradepanel', 'tradepanel', 'huong_dan', 1.0),
                kq('/sudung/tradepanel', 'tradepanel', 'su_dung', 0.9)]


def _dien_giai_ro(cau_hoi, cac_doan, ngu_canh):
    _seen['ghi_chu'] = ngu_canh.ghi_chu
    # Cố tình khai nguồn — lượt hỏi làm rõ vẫn KHÔNG được gắn.
    return 'Anh cần phần nào: (1) cài đặt, (2) vào lệnh / chốt lời / dời SL, (3) báo lỗi?\nNGUON_DA_DUNG: 1, 2'


mh.dien_giai = _dien_giai_ro
tl.lay_bo_tim = lambda **k: _BoTimCoDoan()
tl.khop = lambda cau, nc: None
luu = {}
tl.luu_phien = lambda phien, **k: luu.update(k) or True
nc7 = NguCanh(phien='thu-nguon-8')
kq7 = tl.tra_loi('quang trade panel dùng như thế nào', nc7)
kiem('KHÔNG hướng dẫn' in _seen.get('ghi_chu', ''), 'mô hình bị dặn chỉ hỏi làm rõ')
kiem(kq7.nguon == [] and 'Nguồn' not in kq7.tra_loi, 'không gắn "Nguồn tham khảo" dù mô hình khai')
kiem(luu.get('so_tay', {}).get('vua_hoi_lam_ro') is True, 'sổ tay ghi "vừa hỏi làm rõ"')

_seen.clear()
nc8 = NguCanh(phien='thu-nguon-8', so_tay=dict(luu['so_tay']))
tl.tra_loi('dùng như thế nào', nc8)
kiem('KHÔNG hướng dẫn' not in (_seen.get('ghi_chu') or ''),
     'khách vẫn đáp chung chung -> KHÔNG hỏi lại lần hai, trả lời luôn')

_seen.clear()
tl.tra_loi('trade panel chốt 50% là sao', NguCanh(phien='thu-nguon-9'))
kiem('KHÔNG hướng dẫn' not in (_seen.get('ghi_chu') or ''), 'câu có việc cụ thể -> trả lời bình thường')

# Chủ dự án gõ NGUYÊN tên sản phẩm trên trang web (có "MT5", "Telegram"...).
from giao_uoc import SanPham  # noqa: E402
_TEN = {'signaltrader': 'Telegram, Discord to MT5 Signal Copier',
        'signalbroadcaster': 'MT5, Telegram, Discord to Telegram, Discord Signal Broadcaster'}
tl.tim_san_pham = lambda s: (SanPham(ma=s, slug=s, ten=_TEN[s], ten_vi=_TEN[s], backend='', phien_ban='')
                             if s in _TEN else None)
for cau in ('Telegram, Discord to MT5 Signal Copier dùng như thế nào',
            'MT5, Telegram, Discord to Telegram, Discord Signal Broadcaster dùng như thế nào'):
    kiem(tl._cau_hoi_chung_chung(cau, NguCanh(phien='x')), f'tên đầy đủ vẫn nhận ra câu chung chung: {cau[:40]!r}')
kiem(not tl._cau_hoi_chung_chung('signal copier lấy tín hiệu kênh riêng tư được không', NguCanh(phien='x')),
     'câu cụ thể về signal copier -> không hỏi lại')

print('\n=== 8) Cắt khối "Nguồn tham khảo" mô hình tự viết ===')
from nao.tra_loi import cat_khoi_nguon  # noqa: E402
vao = ('Dạ bật AutoTrading.\n\n1. Bước một\n2. Bước hai\n\nNguồn tham khảo:\n'
       '- Cài đặt: /guilde/copytrade\n- FAQ: /faq/faqcopytrade\n\nAnh cần gì thêm ạ?')
ra = cat_khoi_nguon(vao)
kiem('Nguồn' not in ra and '/guilde' not in ra and 'Bước hai' in ra and ra.endswith('Anh cần gì thêm ạ?'),
     'cắt tiêu đề + các dòng liệt kê theo sau, giữ phần trước và sau')
for dau in ('**Nguồn tham khảo:**', '### Nguồn', 'Nguồn: /guilde/tradepanel', 'Sources:'):
    kiem('/x' not in cat_khoi_nguon(f'Thân.\n{dau}\n- a: /x'), f'bắt được tiêu đề dạng {dau!r}')
giu = ('1. Khai **nguồn** (một, hai hay cả ba):\n'
       '**Nguồn kênh Telegram:** đăng nhập, nhập mã hai lần.\n'
       '- Nguồn MT5: một tài khoản ↔ một terminal.\n'
       'Nguồn tin ở trên, kênh phát ở dưới.')
kiem(cat_khoi_nguon(giu) == giu, 'KHÔNG cắt câu thường có chữ "nguồn" (nguồn kênh, nguồn MT5, nguồn tin)')

print()
if truot:
    print(f'THU_NGUON: {truot} phép thử TRƯỢT.')
    sys.exit(1)
print('THU_NGUON: QUA HẾT.')
