"""ابزارهای مشترک متن (بدون نیاز به torch).

منابع آموزش: data/train.txt (دستی) و data/site_memory.txt (حافظهٔ سایت هوشمارک؛ فقط از طریق PUT /api/memory نوشته می‌شود).

قالب هر دو فایل:
  - هر خط یک نمونهٔ آموزشی است؛ خط خالی و خط‌هایی که با # شروع شوند نادیده گرفته می‌شوند.
  - خط گفتگو:  کاربر: سؤال | مدل: جواب
  - هر خط دیگر به‌صورت متن ساده در آموزش استفاده می‌شود.
"""
from pathlib import Path
import re

BASE = Path(__file__).resolve().parent
TRAIN_FILE = BASE / 'data' / 'train.txt'
SITE_MEMORY_FILE = BASE / 'data' / 'site_memory.txt'

BLOCK = 128  # طول بافت مدل (نویسه)؛ train.py از همین مقدار استفاده می‌کند. هر نمونهٔ آموزشی (با خط‌جدید) باید در آن جا شود

_PUNCT = re.compile(r'[؟?!.,،؛:|"«»()]')
_DIALOGUE = re.compile(r'^\s*کاربر\s*:\s*(.*?)\s*\|\s*مدل\s*:\s*(.+?)\s*$')


def fix_chars(text: str) -> str:
    """ی و ک عربی را به فارسی تبدیل می‌کند."""
    return text.replace('ي', 'ی').replace('ك', 'ک')


def normalize_question(text: str) -> str:
    """یکسان‌سازی سؤال: ی/ک، نیم‌فاصله، علائم نگارشی، فاصله‌های اضافه، حروف بزرگ."""
    t = fix_chars(text).replace('\u200c', ' ')
    t = _PUNCT.sub(' ', t)
    return re.sub(r'\s+', ' ', t).strip().lower()


def build_prompt(question: str) -> str:
    """ابتدای متنی که مدل باید ادامه‌اش را بسازد (هم در آموزش، هم هنگام پاسخ‌دهی)."""
    return f'کاربر: {normalize_question(question)} | مدل:'


def parse_dialogue(line: str):
    """اگر خط به قالب «کاربر: ... | مدل: ...» باشد (سؤال، جواب) برمی‌گرداند، وگرنه None."""
    m = _DIALOGUE.match(line)
    if not m or not m.group(1).strip():
        return None
    return m.group(1), m.group(2)


def load_training_lines(path: Path = TRAIN_FILE, include_site_memory: bool = True):
    """خط‌های آمادهٔ آموزش از train.txt و (در حالت پیش‌فرض) data/site_memory.txt."""
    paths = [path]
    if include_site_memory and path == TRAIN_FILE:
        paths.append(SITE_MEMORY_FILE)
    lines = []
    for p in paths:
        if not p.exists():
            continue
        for raw in p.read_text(encoding='utf-8').splitlines():
            line = raw.strip()
            if not line or line.startswith('#'):
                continue
            line = fix_chars(line)
            pair = parse_dialogue(line)
            if pair:
                line = f"{build_prompt(pair[0])} {' '.join(pair[1].split())}"
            else:
                line = ' '.join(line.split())
            lines.append(line)
    return lines


def load_pairs(path: Path = TRAIN_FILE):
    """فقط جفت‌های (سؤال نرمال‌شده، جواب) از خط‌های گفتگو؛ برای تست مدل."""
    pairs = []
    if not path.exists():
        return pairs
    for raw in path.read_text(encoding='utf-8').splitlines():
        line = fix_chars(raw.strip())
        if not line or line.startswith('#'):
            continue
        pair = parse_dialogue(line)
        if pair:
            pairs.append((normalize_question(pair[0]), ' '.join(pair[1].split())))
    return pairs
