/**
 * موتور نرمال‌سازی متن فارسی.
 * تمام متن‌های ورودی کاربر و محتوای ایندکس‌شده از این مسیر عبور می‌کنند تا
 * جست‌وجوی «اب» و «آب»، «ي» و «ی»، «۱۴۰۵» و «1405» به نتیجه‌ی یکسان برسد.
 */

const ARABIC_TO_PERSIAN: Record<string, string> = {
  'ي': 'ی', 'ى': 'ی', 'ﯼ': 'ی', 'ﯽ': 'ی', 'ﻳ': 'ی', 'ﻲ': 'ی',
  'ك': 'ک', 'ﻛ': 'ک', 'ﮐ': 'ک', 'ﻚ': 'ک',
  'ة': 'ه', 'ۀ': 'ه',
  'ؤ': 'و', 'إ': 'ا', 'أ': 'ا', 'آ': 'ا', 'ٱ': 'ا',
  'ئ': 'ی',
};

const PERSIAN_DIGITS = '۰۱۲۳۴۵۶۷۸۹';
const ARABIC_DIGITS = '٠١٢٣٤٥٦٧٨٩';

/** حذف اعراب، کشیده و نویسه‌های کنترلی (ZWNJ/ZWJ/RLM/LRM) */
const DIACRITICS = /[\u064B-\u065F\u0670\u0640\u06D6-\u06ED]/g;
const ZERO_WIDTH = /[\u200B\u200C\u200D\u200E\u200F\uFEFF]/g;
const PUNCTUATION = /[.,،؛;:!؟?()[\]{}«»"'`~^*_\-–—/\\|+=<>@#$%&]/g;

/** تبدیل ارقام فارسی/عربی به لاتین */
export function normalizeDigits(input: string): string {
  let out = '';
  for (const ch of input) {
    const p = PERSIAN_DIGITS.indexOf(ch);
    if (p >= 0) { out += String(p); continue; }
    const a = ARABIC_DIGITS.indexOf(ch);
    if (a >= 0) { out += String(a); continue; }
    out += ch;
  }
  return out;
}

/** نمایش ارقام لاتین به‌صورت فارسی (برای خروجی‌های سمت سرور مثل کد رهگیری) */
export function toPersianDigits(input: string | number): string {
  return String(input).replace(/[0-9]/g, (d) => PERSIAN_DIGITS[Number(d)]);
}

/**
 * نرمال‌سازی کامل: حروف عربی → فارسی، ارقام → لاتین، حذف اعراب و نویسه‌های صفرعرض،
 * تبدیل نیم‌فاصله به فاصله و یکسان‌سازی فاصله‌ها.
 */
export function normalizePersian(input: string): string {
  if (!input) return '';
  let text = input.normalize('NFKC');
  text = text.replace(ZERO_WIDTH, ' ');
  text = text.replace(DIACRITICS, '');
  text = normalizeDigits(text);
  text = Array.from(text).map((ch) => ARABIC_TO_PERSIAN[ch] ?? ch).join('');
  text = text.replace(PUNCTUATION, ' ');
  text = text.toLowerCase();
  return text.replace(/\s+/g, ' ').trim();
}

/** توکن‌سازی ساده با حذف کلمات پرتکرار بی‌اثر */
const STOPWORDS = new Set([
  'و', 'در', 'به', 'از', 'که', 'با', 'را', 'این', 'آن', 'است', 'برای', 'تا', 'هم',
  'یک', 'های', 'ها', 'می', 'بر', 'بود', 'شد', 'شده', 'کرد', 'کند', 'نیز',
]);

export function tokenize(input: string): string[] {
  return normalizePersian(input)
    .split(' ')
    .filter((t) => t.length > 1 && !STOPWORDS.has(t));
}

/** فاصله لِوِنشتاین برای تحمل غلط تایپی */
export function levenshtein(a: string, b: string): number {
  if (a === b) return 0;
  if (!a.length) return b.length;
  if (!b.length) return a.length;
  let prev = Array.from({ length: b.length + 1 }, (_, i) => i);
  for (let i = 1; i <= a.length; i++) {
    const curr = [i];
    for (let j = 1; j <= b.length; j++) {
      curr[j] = Math.min(
        prev[j] + 1,
        curr[j - 1] + 1,
        prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1),
      );
    }
    prev = curr;
  }
  return prev[b.length];
}

/** آستانه تحمل غلط بر اساس طول کلمه (کلمه کوتاه = تحمل کمتر) */
export function fuzzyMatches(token: string, candidate: string): boolean {
  // زیررشته فقط برای توکن‌های بلند پذیرفته می‌شود؛ در غیر این صورت «آب» با
  // «خیابان» هم مطابقت پیدا می‌کرد.
  if (token.length >= 4 && candidate.includes(token)) return true;
  const tolerance = token.length <= 3 ? 0 : token.length <= 6 ? 1 : 2;
  if (tolerance === 0) return candidate.split(' ').some((w) => w === token);
  for (const word of candidate.split(' ')) {
    if (Math.abs(word.length - token.length) > tolerance) continue;
    if (levenshtein(token, word) <= tolerance) return true;
  }
  return false;
}

/** تولید یک اسلاگ فارسی امن برای URL (حروف فارسی حفظ می‌شوند) */
export function persianSlug(input: string): string {
  return normalizePersian(input)
    .replace(/[^\p{L}\p{N}\s-]/gu, '')
    .replace(/\s+/g, '-')
    .replace(/-+/g, '-')
    .slice(0, 90) || 'item';
}
