// communications.jsx — Interactive notification, activity and letter center (v31.99)
// This legacy surface has no server-side communications API yet. The module
// persists UI state and local drafts per guest device without pretending that
// a real external message was delivered.
/* global React, Ic, fmt, AudioCtl */
/* eslint-disable react/jsx-no-undef -- This classic browser bundle receives React and Ic from earlier UMD scripts. */

const SH_COMM_SCHEMA = 1;
const SH_COMM_EVENT = "shahdad:communications-updated";
const SH_COMM_NAV_EVENT = "shahdad:communications-navigate";
const SH_COMM_STORAGE_PREFIX = "shahdad_communications_v1";
let shCommLastWriteSucceeded = true;

const shCommLocal = (lang, value) => {
  if (value === null || value === undefined) return "";
  if (typeof value !== "object") return String(value);
  return value[lang] || value.fa || value.en || value.ar || "";
};

const SH_COMM_COPY = {
  title: { fa: "مرکز ارتباطات و پیگیری", en: "Communications & Follow-up", ar: "مركز الاتصالات والمتابعة" },
  subtitle: {
    fa: "اعلان‌ها، رویدادها و مکاتبات حقوقی در یک نمای متمرکز",
    en: "Legal alerts, events and correspondence in one focused workspace",
    ar: "التنبيهات والأحداث والمراسلات القانونية في مساحة مركزية واحدة"
  },
  notifications: { fa: "اعلان‌ها", en: "Notifications", ar: "الإشعارات" },
  activities: { fa: "آخرین فعالیت‌ها", en: "Recent activity", ar: "أحدث الأنشطة" },
  letters: { fa: "نامه‌ها", en: "Letters", ar: "الرسائل" },
  unread: { fa: "خوانده‌نشده", en: "Unread", ar: "غير مقروء" },
  today: { fa: "رویداد امروز", en: "Events today", ar: "أحداث اليوم" },
  inbox: { fa: "صندوق ورودی", en: "Inbox", ar: "الوارد" },
  compose: { fa: "نامه جدید", en: "New letter", ar: "رسالة جديدة" },
  search: { fa: "جست‌وجو در این بخش…", en: "Search this section…", ar: "ابحث في هذا القسم…" },
  markAllRead: { fa: "خواندن همه", en: "Mark all read", ar: "تعليم الكل كمقروء" },
  selected: { fa: "انتخاب‌شده", en: "Selected", ar: "محدد" },
  selectAll: { fa: "انتخاب همه", en: "Select all", ar: "تحديد الكل" },
  markRead: { fa: "خوانده شد", en: "Mark read", ar: "تعليم كمقروء" },
  markUnread: { fa: "خوانده‌نشده", en: "Mark unread", ar: "تعليم كغير مقروء" },
  star: { fa: "ستاره‌دار", en: "Star", ar: "تمييز بنجمة" },
  unstar: { fa: "حذف ستاره", en: "Remove star", ar: "إزالة النجمة" },
  archive: { fa: "بایگانی", en: "Archive", ar: "أرشفة" },
  restore: { fa: "بازگردانی از بایگانی", en: "Restore from archive", ar: "استعادة من الأرشيف" },
  snooze: { fa: "یادآوری فردا", en: "Remind tomorrow", ar: "تذكير غداً" },
  openRelated: { fa: "رفتن به بخش مرتبط", en: "Open related section", ar: "فتح القسم المرتبط" },
  copyLink: { fa: "کپی پیوند", en: "Copy link", ar: "نسخ الرابط" },
  reply: { fa: "پاسخ", en: "Reply", ar: "رد" },
  forward: { fa: "ارسال مجدد", en: "Forward", ar: "إعادة إرسال" },
  noResults: { fa: "موردی با این جست‌وجو و فیلتر پیدا نشد.", en: "No items match this search and filter.", ar: "لا توجد نتائج مطابقة." },
  chooseItem: { fa: "یک مورد را برای مشاهده جزئیات انتخاب کنید.", en: "Select an item to view its details.", ar: "اختر عنصراً لعرض التفاصيل." },
  undo: { fa: "بازگردانی", en: "Undo", ar: "تراجع" },
  localMode: { fa: "دادهٔ نمایشی و محلی — بدون ارسال بیرونی", en: "Local demo data — no external delivery", ar: "بيانات تجريبية محلية — دون إرسال خارجي" },
  savedLocal: { fa: "تغییرات روی این دستگاه ذخیره شد.", en: "Changes were saved on this device.", ar: "تم حفظ التغييرات على هذا الجهاز." },
  persistenceFailed: {
    fa: "تغییر اعمال شد، اما مرورگر اجازهٔ ذخیرهٔ دائمی نداد.",
    en: "The change was applied, but the browser could not persist it.",
    ar: "تم تطبيق التغيير، لكن المتصفح لم يسمح بالحفظ الدائم."
  },
  privacyWarning: {
    fa: "این نسخه نمایشی است؛ متن نامه و نام پیوست فقط در حافظهٔ مرورگر و بدون رمزنگاری سرور ذخیره می‌شود. اطلاعات محرمانه وارد نکنید.",
    en: "This is a demo. Letter text and attachment names stay in browser storage without server-side encryption. Do not enter confidential information.",
    ar: "هذه نسخة تجريبية. يُحفظ نص الرسالة وأسماء المرفقات في المتصفح دون تشفير من الخادم. لا تُدخل معلومات سرية."
  },
  demo: { fa: "نمونه", en: "Demo", ar: "تجريبي" },
  linkCopied: { fa: "پیوند مستقیم کپی شد.", en: "Direct link copied.", ar: "تم نسخ الرابط المباشر." },
  linkCopyFailed: { fa: "کپی خودکار ممکن نبود.", en: "Automatic copy was unavailable.", ar: "تعذر النسخ التلقائي." },
  back: { fa: "بازگشت به فهرست", en: "Back to list", ar: "العودة إلى القائمة" }
};

const SH_COMM_NOTIFICATION_SEED = [
  {
    id: "notification-hearing-2487",
    kind: "danger",
    category: "deadline",
    title: { fa: "جلسه داوری پرونده ۲۴۸۷ فردا برگزار می‌شود", en: "Case 2487 arbitration hearing is tomorrow", ar: "جلسة تحكيم القضية ٢٤٨٧ غداً" },
    body: {
      fa: "جلسه ساعت ۱۰:۰۰ در شعبه مرکزی برگزار می‌شود. مدارک هویتی و آخرین لایحه را همراه داشته باشید.",
      en: "The hearing starts at 10:00 at the central branch. Bring identification and the latest brief.",
      ar: "تبدأ الجلسة الساعة ١٠:٠٠ في الفرع المركزي. أحضر الهوية وآخر مذكرة."
    },
    time: Date.now() - 8 * 60 * 1000,
    timeText: { fa: "۸ دقیقه پیش", en: "8 min ago", ar: "قبل ٨ دقائق" },
    unread: true,
    starred: true,
    archived: false,
    action: { module: "clock", label: { fa: "مشاهده در تقویم", en: "View in calendar", ar: "عرض في التقويم" } },
    meta: { fa: "فوری · فردا ساعت ۱۰:۰۰", en: "Urgent · Tomorrow at 10:00", ar: "عاجل · غداً ١٠:٠٠" }
  },
  {
    id: "notification-tax-update",
    kind: "warn",
    category: "regulation",
    title: { fa: "تغییرات تازه در مقررات مالیاتی", en: "New tax regulation changes", ar: "تغييرات جديدة في اللوائح الضريبية" },
    body: {
      fa: "خلاصه تغییرات مرتبط با قراردادها و حق‌الوکاله آماده بررسی است.",
      en: "A summary of changes affecting contracts and legal fees is ready for review.",
      ar: "ملخص التغييرات المتعلقة بالعقود والأتعاب جاهز للمراجعة."
    },
    time: Date.now() - 36 * 60 * 1000,
    timeText: { fa: "۳۶ دقیقه پیش", en: "36 min ago", ar: "قبل ٣٦ دقيقة" },
    unread: true,
    starred: false,
    archived: false,
    action: { module: "calc", label: { fa: "بررسی محاسبات", en: "Review calculators", ar: "مراجعة الحسابات" } },
    meta: { fa: "مقررات · مالیات", en: "Regulation · Tax", ar: "لوائح · ضرائب" }
  },
  {
    id: "notification-signature",
    kind: "info",
    category: "document",
    title: { fa: "سند همکاری آماده امضای شماست", en: "Partnership document is ready for signature", ar: "وثيقة الشراكة جاهزة للتوقيع" },
    body: {
      fa: "نسخه نهایی سند C-1019 پس از بررسی طرف مقابل برای امضا آماده شده است.",
      en: "The final C-1019 document is ready after the counterparty review.",
      ar: "النسخة النهائية من الوثيقة C-1019 جاهزة بعد مراجعة الطرف الآخر."
    },
    time: Date.now() - 2 * 60 * 60 * 1000,
    timeText: { fa: "۲ ساعت پیش", en: "2 hours ago", ar: "قبل ساعتين" },
    unread: true,
    starred: false,
    archived: false,
    action: { module: "contracts", label: { fa: "باز کردن سند", en: "Open document", ar: "فتح الوثيقة" } },
    meta: { fa: "سند C-1019", en: "Document C-1019", ar: "الوثيقة C-1019" }
  },
  {
    id: "notification-plan-renewed",
    kind: "ok",
    category: "account",
    title: { fa: "اشتراک سپر حقوقی تمدید شد", en: "Legal Shield subscription renewed", ar: "تم تجديد اشتراك الدرع القانوني" },
    body: {
      fa: "تمدید با موفقیت ثبت شد و دسترسی خدمات بدون وقفه ادامه دارد.",
      en: "The renewal was recorded successfully and service access continues.",
      ar: "تم تسجيل التجديد بنجاح ويستمر الوصول إلى الخدمات."
    },
    time: Date.now() - 22 * 60 * 60 * 1000,
    timeText: { fa: "دیروز", en: "Yesterday", ar: "أمس" },
    unread: false,
    starred: false,
    archived: false,
    action: { module: "plans", label: { fa: "مشاهده پلن", en: "View plan", ar: "عرض الخطة" } },
    meta: { fa: "حساب کاربری", en: "Account", ar: "الحساب" }
  },
  {
    id: "notification-case-deadline",
    kind: "warn",
    category: "deadline",
    title: { fa: "سه روز تا مهلت ارسال لایحه باقی مانده", en: "Three days remain to submit the brief", ar: "تبقى ثلاثة أيام لتقديم المذكرة" },
    body: {
      fa: "مهلت پرونده اختلاف ملکی مشهد در ۳ روز آینده پایان می‌یابد.",
      en: "The Mashhad property dispute deadline expires in three days.",
      ar: "تنتهي مهلة نزاع العقار في مشهد خلال ثلاثة أيام."
    },
    time: Date.now() - 28 * 60 * 60 * 1000,
    timeText: { fa: "۱ روز پیش", en: "1 day ago", ar: "قبل يوم" },
    unread: false,
    starred: true,
    archived: false,
    action: { module: "case", label: { fa: "مشاهده پرونده", en: "Open case", ar: "فتح القضية" } },
    meta: { fa: "پرونده ۲۴۸۷", en: "Case 2487", ar: "القضية ٢٤٨٧" }
  }
];

const SH_COMM_ACTIVITY_SEED = [
  {
    id: "activity-arbitration-created",
    category: "case",
    title: { fa: "پرونده جدید در داوری ثبت شد", en: "New arbitration case filed", ar: "تم تسجيل قضية تحكيم جديدة" },
    body: { fa: "پرونده ۲۴۹۱ با اولویت بالا ایجاد و به کارتابل شما افزوده شد.", en: "Case 2491 was created with high priority and added to your workspace.", ar: "تم إنشاء القضية ٢٤٩١ بأولوية عالية وإضافتها إلى مساحة عملك." },
    actor: { fa: "سامانه پرونده", en: "Case system", ar: "نظام القضايا" },
    time: Date.now() - 2 * 60 * 1000,
    timeText: { fa: "۲ دقیقه پیش", en: "2 min ago", ar: "قبل دقيقتين" },
    unread: false,
    starred: false,
    archived: false,
    action: { module: "case", label: { fa: "باز کردن پرونده", en: "Open case", ar: "فتح القضية" } },
    details: [
      { fa: "شناسه پرونده: ۲۴۹۱", en: "Case ID: 2491", ar: "رقم القضية: ٢٤٩١" },
      { fa: "اولویت: بالا", en: "Priority: High", ar: "الأولوية: عالية" },
      { fa: "مرحله: بررسی اولیه", en: "Stage: Initial review", ar: "المرحلة: مراجعة أولية" }
    ]
  },
  {
    id: "activity-consultation",
    category: "consultation",
    title: { fa: "درخواست مشاوره جدید دریافت شد", en: "New consultation request received", ar: "تم استلام طلب استشارة جديد" },
    body: { fa: "یک درخواست مشاوره متنی درباره اختلاف قراردادی در انتظار بررسی است.", en: "A text consultation about a contract dispute is awaiting review.", ar: "طلب استشارة نصية حول نزاع تعاقدي بانتظار المراجعة." },
    actor: { fa: "مرکز مشاوره", en: "Consultation center", ar: "مركز الاستشارات" },
    time: Date.now() - 10 * 60 * 1000,
    timeText: { fa: "۱۰ دقیقه پیش", en: "10 min ago", ar: "قبل ١٠ دقائق" },
    unread: false,
    starred: true,
    archived: false,
    action: { module: "consult", label: { fa: "بررسی درخواست", en: "Review request", ar: "مراجعة الطلب" } },
    details: [
      { fa: "نوع: مشاوره متنی", en: "Type: Text consultation", ar: "النوع: استشارة نصية" },
      { fa: "موضوع: اختلاف قرارداد", en: "Topic: Contract dispute", ar: "الموضوع: نزاع تعاقدي" }
    ]
  },
  {
    id: "activity-contract-ai",
    category: "document",
    title: { fa: "قرارداد توسط هوش مصنوعی تحلیل شد", en: "Contract analyzed by AI", ar: "تم تحليل العقد بالذكاء الاصطناعي" },
    body: { fa: "تحلیل قرارداد C-1024 کامل شد و سه بند پرریسک شناسایی شد.", en: "Analysis of contract C-1024 finished with three high-risk clauses.", ar: "اكتمل تحليل العقد C-1024 وتم تحديد ثلاثة بنود عالية المخاطر." },
    actor: { fa: "دستیار تحلیل سند", en: "Document analysis assistant", ar: "مساعد تحليل المستندات" },
    time: Date.now() - 25 * 60 * 1000,
    timeText: { fa: "۲۵ دقیقه پیش", en: "25 min ago", ar: "قبل ٢٥ دقيقة" },
    unread: false,
    starred: false,
    archived: false,
    action: { module: "contracts", label: { fa: "مشاهده تحلیل", en: "View analysis", ar: "عرض التحليل" } },
    details: [
      { fa: "امتیاز ریسک: ۳۲ از ۱۰۰", en: "Risk score: 32/100", ar: "درجة المخاطر: ٣٢ من ١٠٠" },
      { fa: "۳ بند نیازمند بازبینی", en: "3 clauses need review", ar: "٣ بنود تحتاج إلى مراجعة" }
    ]
  },
  {
    id: "activity-payment",
    category: "financial",
    title: { fa: "پرداخت جدید در پرونده ۲۴۸۷ ثبت شد", en: "New payment recorded on case 2487", ar: "تم تسجيل دفعة جديدة في القضية ٢٤٨٧" },
    body: { fa: "رسید پرداخت هزینه کارشناسی در گردش مالی پرونده ثبت شد.", en: "The expert-fee receipt was recorded in the case ledger.", ar: "تم تسجيل إيصال أتعاب الخبير في السجل المالي للقضية." },
    actor: { fa: "کیف پول شهداد", en: "Shahdad Wallet", ar: "محفظة شهداد" },
    time: Date.now() - 60 * 60 * 1000,
    timeText: { fa: "۱ ساعت پیش", en: "1 hour ago", ar: "قبل ساعة" },
    unread: false,
    starred: false,
    archived: false,
    action: { module: "wallet", label: { fa: "مشاهده رسید", en: "View receipt", ar: "عرض الإيصال" } },
    details: [
      { fa: "نوع: هزینه کارشناسی", en: "Type: Expert fee", ar: "النوع: أتعاب خبير" },
      { fa: "وضعیت: ثبت‌شده", en: "Status: Recorded", ar: "الحالة: مسجل" }
    ]
  },
  {
    id: "activity-profile",
    category: "account",
    title: { fa: "اطلاعات هویتی بازبینی شد", en: "Identity information reviewed", ar: "تمت مراجعة معلومات الهوية" },
    body: { fa: "بازبینی خودکار اطلاعات پایه کامل شد؛ تغییری نیاز نیست.", en: "The automated review completed; no changes are required.", ar: "اكتملت المراجعة الآلية ولا يلزم أي تغيير." },
    actor: { fa: "مرکز هویت", en: "Identity center", ar: "مركز الهوية" },
    time: Date.now() - 3 * 60 * 60 * 1000,
    timeText: { fa: "۳ ساعت پیش", en: "3 hours ago", ar: "قبل ٣ ساعات" },
    unread: false,
    starred: false,
    archived: false,
    action: { module: "profile", label: { fa: "مشاهده پروفایل", en: "View profile", ar: "عرض الملف الشخصي" } },
    details: [{ fa: "نتیجه: بدون مغایرت", en: "Result: No mismatch", ar: "النتيجة: لا يوجد تعارض" }]
  },
  {
    id: "activity-reminder",
    category: "calendar",
    title: { fa: "یادآور مهلت لایحه تنظیم شد", en: "Brief deadline reminder created", ar: "تم إنشاء تذكير بموعد المذكرة" },
    body: { fa: "یادآور برای ۲۴ ساعت پیش از پایان مهلت فعال شد.", en: "A reminder was enabled for 24 hours before the deadline.", ar: "تم تفعيل تذكير قبل ٢٤ ساعة من الموعد النهائي." },
    actor: { fa: "تقویم حقوقی", en: "Legal calendar", ar: "التقويم القانوني" },
    time: Date.now() - 26 * 60 * 60 * 1000,
    timeText: { fa: "دیروز", en: "Yesterday", ar: "أمس" },
    unread: false,
    starred: false,
    archived: false,
    action: { module: "clock", label: { fa: "مشاهده یادآور", en: "View reminder", ar: "عرض التذكير" } },
    details: [{ fa: "زمان یادآوری: ۲۴ ساعت قبل", en: "Reminder: 24 hours before", ar: "التذكير: قبل ٢٤ ساعة" }]
  }
];

const SH_COMM_LETTER_SEED = [
  {
    id: "letter-arbitration-invite",
    folder: "inbox",
    from: { fa: "دفتر داوری مرکزی", en: "Central Arbitration Office", ar: "مكتب التحكيم المركزي" },
    to: { fa: "شما", en: "You", ar: "أنت" },
    subject: { fa: "دعوت‌نامه جلسه داوری پرونده ۲۴۸۷", en: "Arbitration hearing invitation — case 2487", ar: "دعوة جلسة التحكيم — القضية ٢٤٨٧" },
    body: {
      fa: "با سلام، جلسه رسیدگی پرونده ۲۴۸۷ فردا ساعت ۱۰:۰۰ برگزار می‌شود. لطفاً مدارک هویتی، آخرین لایحه و پیوست‌های مرتبط را همراه داشته باشید.",
      en: "The hearing for case 2487 will be held tomorrow at 10:00. Please bring identification, the latest brief and supporting attachments.",
      ar: "ستعقد جلسة القضية ٢٤٨٧ غداً الساعة ١٠:٠٠. يرجى إحضار الهوية وآخر مذكرة والمرفقات ذات الصلة."
    },
    time: Date.now() - 14 * 60 * 1000,
    timeText: { fa: "۱۴ دقیقه پیش", en: "14 min ago", ar: "قبل ١٤ دقيقة" },
    unread: true,
    starred: true,
    priority: "high",
    archived: false,
    attachments: ["دعوت‌نامه-جلسه.pdf"]
  },
  {
    id: "letter-lawyer-review",
    folder: "inbox",
    from: { fa: "دکتر سارا کریمی", en: "Dr. Sara Karimi", ar: "د. سارة كريمي" },
    to: { fa: "شما", en: "You", ar: "أنت" },
    subject: { fa: "نسخه بازبینی‌شده لایحه", en: "Reviewed version of the brief", ar: "النسخة المنقحة من المذكرة" },
    body: {
      fa: "نسخه بازبینی‌شده لایحه پیوست شد. بند سوم بر اساس آخرین مستند پرونده اصلاح شده است. لطفاً تا پایان امروز نظر نهایی را اعلام کنید.",
      en: "The reviewed brief is attached. Clause three was updated using the latest case document. Please share your final feedback today.",
      ar: "تم إرفاق المذكرة المنقحة. تم تحديث البند الثالث وفق أحدث مستند في القضية. يرجى إرسال رأيك النهائي اليوم."
    },
    time: Date.now() - 44 * 60 * 1000,
    timeText: { fa: "۴۴ دقیقه پیش", en: "44 min ago", ar: "قبل ٤٤ دقيقة" },
    unread: true,
    starred: false,
    priority: "normal",
    archived: false,
    attachments: ["لایحه-بازبینی-نهایی.docx"]
  },
  {
    id: "letter-system-report",
    folder: "inbox",
    from: { fa: "سامانه شهداد", en: "Shahdad System", ar: "نظام شهداد" },
    to: { fa: "شما", en: "You", ar: "أنت" },
    subject: { fa: "گزارش هفتگی پرونده‌ها", en: "Weekly case report", ar: "تقرير القضايا الأسبوعي" },
    body: {
      fa: "گزارش هفتگی آماده است: دو مهلت نزدیک، یک سند آماده امضا و سه فعالیت جدید ثبت شده است.",
      en: "Your weekly report is ready: two upcoming deadlines, one document awaiting signature and three new activities.",
      ar: "تقريرك الأسبوعي جاهز: موعدان قريبان ووثيقة بانتظار التوقيع وثلاثة أنشطة جديدة."
    },
    time: Date.now() - 5 * 60 * 60 * 1000,
    timeText: { fa: "۵ ساعت پیش", en: "5 hours ago", ar: "قبل ٥ ساعات" },
    unread: true,
    starred: false,
    priority: "normal",
    archived: false,
    attachments: []
  },
  {
    id: "letter-sent-documents",
    folder: "sent",
    from: { fa: "شما", en: "You", ar: "أنت" },
    to: { fa: "دفتر داوری مرکزی", en: "Central Arbitration Office", ar: "مكتب التحكيم المركزي" },
    subject: { fa: "ارسال مستندات تکمیلی پرونده ۲۴۸۷", en: "Additional documents for case 2487", ar: "مستندات إضافية للقضية ٢٤٨٧" },
    body: {
      fa: "مستندات تکمیلی مطابق درخواست ارسال شد. لطفاً دریافت فایل‌ها را تأیید فرمایید.",
      en: "The requested additional documents were sent. Please confirm receipt.",
      ar: "تم إرسال المستندات الإضافية المطلوبة. يرجى تأكيد الاستلام."
    },
    time: Date.now() - 20 * 60 * 60 * 1000,
    timeText: { fa: "امروز، ۰۸:۳۰", en: "Today, 08:30", ar: "اليوم، ٠٨:٣٠" },
    unread: false,
    starred: false,
    priority: "normal",
    archived: false,
    attachments: ["مستندات-تکمیلی.zip"]
  },
  {
    id: "letter-draft-followup",
    folder: "draft",
    from: { fa: "شما", en: "You", ar: "أنت" },
    to: { fa: "دکتر سارا کریمی", en: "Dr. Sara Karimi", ar: "د. سارة كريمي" },
    subject: { fa: "پیگیری بازبینی لایحه", en: "Follow-up on brief review", ar: "متابعة مراجعة المذكرة" },
    body: {
      fa: "سلام، بند سوم بررسی شد و نکته‌ای درباره تاریخ مستند دارم…",
      en: "Hello, I reviewed clause three and have a note about the document date…",
      ar: "مرحباً، راجعت البند الثالث ولدي ملاحظة حول تاريخ المستند…"
    },
    time: Date.now() - 30 * 60 * 60 * 1000,
    timeText: { fa: "پیش‌نویس دیروز", en: "Draft from yesterday", ar: "مسودة من أمس" },
    unread: false,
    starred: false,
    priority: "normal",
    archived: false,
    attachments: []
  }
];

const shCommScope = (session) => {
  const raw = session && typeof session.guestId === "string" ? session.guestId : "browser";
  return raw.replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 64) || "browser";
};

const shCommStorageKey = (session) => `${SH_COMM_STORAGE_PREFIX}:${shCommScope(session)}`;

const shCommMergeSeed = (seed, saved, type) => {
  const source = Array.isArray(saved) ? saved.slice(0, 200) : [];
  const byId = new Map(source.filter((item) => item && typeof item.id === "string").map((item) => [item.id, item]));
  const merged = seed.map((base) => {
    const prior = byId.get(base.id) || {};
    const editableSeedDraft = type === "letters" && base.folder === "draft";
    return {
      ...base,
      demo: true,
      ...(editableSeedDraft ? {
        to: typeof prior.to === "string" ? prior.to.slice(0, 120) : base.to,
        subject: typeof prior.subject === "string" ? prior.subject.slice(0, 180) : base.subject,
        body: typeof prior.body === "string" ? prior.body.slice(0, 12000) : base.body,
        time: Number.isFinite(prior.time) ? prior.time : base.time,
        timeText: prior.timeText && typeof prior.timeText === "object" ? prior.timeText : base.timeText,
        attachments: Array.isArray(prior.attachments)
          ? prior.attachments.map((name) => String(name).slice(0, 140)).slice(0, 8)
          : base.attachments
      } : {}),
      unread: typeof prior.unread === "boolean" ? prior.unread : base.unread,
      starred: typeof prior.starred === "boolean" ? prior.starred : base.starred,
      archived: typeof prior.archived === "boolean" ? prior.archived : base.archived,
      snoozedUntil: Number.isFinite(prior.snoozedUntil) ? prior.snoozedUntil : null,
      folder: type === "letters" && ["inbox", "sent", "draft", "archive"].includes(prior.folder) ? prior.folder : base.folder,
      previousFolder: type === "letters" && ["inbox", "sent", "draft"].includes(prior.previousFolder) ? prior.previousFolder : null
    };
  });
  if (type !== "letters") return merged;
  const seedIds = new Set(seed.map((item) => item.id));
  const custom = source
    .filter((item) => item && typeof item.id === "string" && item.id.startsWith("local-letter-") && !seedIds.has(item.id))
    .map((item) => ({
      id: item.id.slice(0, 90),
      folder: ["inbox", "sent", "draft", "archive"].includes(item.folder) ? item.folder : "draft",
      from: String(item.from || "").slice(0, 120),
      to: String(item.to || "").slice(0, 120),
      subject: String(item.subject || "").slice(0, 180),
      body: String(item.body || "").slice(0, 12000),
      time: Number.isFinite(item.time) ? item.time : Date.now(),
      timeText: item.timeText && typeof item.timeText === "object" ? item.timeText : { fa: "ذخیره محلی", en: "Saved locally", ar: "محفوظ محلياً" },
      unread: Boolean(item.unread),
      starred: Boolean(item.starred),
      priority: item.priority === "high" ? "high" : "normal",
      archived: Boolean(item.archived),
      previousFolder: ["inbox", "sent", "draft"].includes(item.previousFolder) ? item.previousFolder : null,
      demo: false,
      attachments: Array.isArray(item.attachments) ? item.attachments.map((name) => String(name).slice(0, 140)).slice(0, 8) : []
    }));
  return [...custom, ...merged];
};

const shCommFreshState = () => ({
  schema: SH_COMM_SCHEMA,
  notifications: SH_COMM_NOTIFICATION_SEED.map((item) => ({ ...item, demo: true })),
  activities: SH_COMM_ACTIVITY_SEED.map((item) => ({ ...item, demo: true })),
  letters: SH_COMM_LETTER_SEED.map((item) => ({ ...item, demo: true }))
});

const shCommNormalizeState = (raw) => {
  if (!raw || typeof raw !== "object" || raw.schema !== SH_COMM_SCHEMA) return shCommFreshState();
  return {
    schema: SH_COMM_SCHEMA,
    notifications: shCommMergeSeed(SH_COMM_NOTIFICATION_SEED, raw.notifications, "notifications"),
    activities: shCommMergeSeed(SH_COMM_ACTIVITY_SEED, raw.activities, "activities"),
    letters: shCommMergeSeed(SH_COMM_LETTER_SEED, raw.letters, "letters")
  };
};

const shCommReadKey = (key) => {
  try {
    const raw = JSON.parse(localStorage.getItem(key) || "null");
    return shCommNormalizeState(raw);
  } catch {
    return shCommFreshState();
  }
};
const shCommRead = (session) => shCommReadKey(shCommStorageKey(session));

const shCommWriteKey = (key, state) => {
  const next = shCommNormalizeState(state);
  shCommLastWriteSucceeded = false;
  try {
    localStorage.setItem(key, JSON.stringify(next));
    shCommLastWriteSucceeded = true;
  } catch {}
  try {
    window.dispatchEvent(new CustomEvent(SH_COMM_EVENT, {
      detail: { key, state: next, persisted: shCommLastWriteSucceeded }
    }));
  } catch {}
  return next;
};
const useShahdadCommunications = (session) => {
  const key = shCommStorageKey(session);
  const [state, setState] = React.useState(() => shCommRead(session));
  const [persistenceOk, setPersistenceOk] = React.useState(true);

  React.useEffect(() => {
    const frame = window.requestAnimationFrame(() => setState(shCommReadKey(key)));
    const sync = (event) => {
      if (event && event.detail && event.detail.key === key) {
        setState(event.detail.state);
        setPersistenceOk(event.detail.persisted !== false);
      }
    };
    const storageSync = (event) => {
      if (event.key === key) setState(shCommReadKey(key));
    };
    window.addEventListener(SH_COMM_EVENT, sync);
    window.addEventListener("storage", storageSync);
    return () => {
      window.cancelAnimationFrame(frame);
      window.removeEventListener(SH_COMM_EVENT, sync);
      window.removeEventListener("storage", storageSync);
    };
  }, [key]);

  const update = React.useCallback((updater) => {
    const current = shCommReadKey(key);
    const candidate = typeof updater === "function" ? updater(current) : updater;
    const next = shCommWriteKey(key, candidate);
    setState(next);
    setPersistenceOk(shCommLastWriteSucceeded);
    return next;
  }, [key]);

  return [state, update, persistenceOk];
};

const shCommFilters = (lang, tab) => {
  const make = (id, fa, en, ar) => ({ id, label: { fa, en, ar } });
  if (tab === "notifications") return [
    make("all", "همه اعلان‌ها", "All alerts", "كل التنبيهات"),
    make("unread", "خوانده‌نشده", "Unread", "غير مقروء"),
    make("urgent", "فوری و مهم", "Urgent", "عاجل ومهم"),
    make("snoozed", "یادآوری‌شده", "Snoozed", "المؤجل"),
    make("starred", "ستاره‌دار", "Starred", "مميز بنجمة"),
    make("archived", "بایگانی", "Archived", "المؤرشف")
  ];
  if (tab === "activities") return [
    make("all", "همه فعالیت‌ها", "All activity", "كل الأنشطة"),
    make("case", "پرونده‌ها", "Cases", "القضايا"),
    make("document", "اسناد", "Documents", "المستندات"),
    make("financial", "مالی", "Financial", "المالية"),
    make("starred", "نشان‌شده", "Starred", "المميزة"),
    make("archived", "بایگانی", "Archived", "المؤرشف")
  ];
  return [
    make("inbox", "صندوق ورودی", "Inbox", "الوارد"),
    make("sent", "ارسال‌شده", "Sent", "المرسل"),
    make("draft", "پیش‌نویس‌ها", "Drafts", "المسودات"),
    make("starred", "ستاره‌دار", "Starred", "المميزة"),
    make("archive", "بایگانی", "Archive", "الأرشيف")
  ];
};

const shCommCollection = (state, tab) => (
  tab === "notifications" ? state.notifications :
  tab === "activities" ? state.activities :
  state.letters
);

const shCommItemText = (item, lang) => [
  shCommLocal(lang, item.title),
  shCommLocal(lang, item.subject),
  shCommLocal(lang, item.body),
  shCommLocal(lang, item.from),
  shCommLocal(lang, item.to),
  shCommLocal(lang, item.actor),
  shCommLocal(lang, item.meta)
].join(" ").toLocaleLowerCase(lang === "fa" ? "fa-IR" : lang === "ar" ? "ar" : "en-US");

const shCommFilteredItems = (state, tab, filter, query, lang, now = Date.now()) => {
  let items = shCommCollection(state, tab).filter((item) => {
    if (tab === "notifications") {
      if (filter === "archived") return item.archived;
      if (item.archived) return false;
      if (filter === "snoozed") return Boolean(item.snoozedUntil && item.snoozedUntil > now);
      if (item.snoozedUntil && item.snoozedUntil > now) return false;
      if (filter === "unread") return item.unread;
      if (filter === "urgent") return item.kind === "danger" || item.starred;
      if (filter === "starred") return item.starred;
      return true;
    }
    if (tab === "activities") {
      if (filter === "archived") return item.archived;
      if (item.archived) return false;
      if (filter === "starred") return item.starred;
      if (filter !== "all") return item.category === filter;
      return true;
    }
    if (filter === "starred") return item.starred && item.folder !== "archive";
    return item.folder === filter;
  });
  const term = query.trim().toLocaleLowerCase(lang === "fa" ? "fa-IR" : lang === "ar" ? "ar" : "en-US");
  if (term) items = items.filter((item) => shCommItemText(item, lang).includes(term));
  return items.slice().sort((a, b) => (b.time || 0) - (a.time || 0));
};

const shCommPreviewItems = (state, tab, now = Date.now()) => {
  const filter = tab === "letters" ? "inbox" : "all";
  return shCommFilteredItems(state, tab, filter, "", "fa", now)
    .sort((a, b) => Number(Boolean(b.unread)) - Number(Boolean(a.unread)) || (b.time || 0) - (a.time || 0))
    .slice(0, 3);
};

const ShCommStatusIcon = ({ tab, item, size = 18 }) => {
  if (tab === "notifications") {
    const Icon = item.kind === "danger" ? Ic.shieldAlert : item.kind === "ok" ? Ic.shieldCheck : Ic.bell;
    return <Icon size={size} />;
  }
  if (tab === "activities") {
    const Icon = item.category === "document" ? Ic.doc :
      item.category === "financial" ? Ic.coin :
      item.category === "consultation" ? Ic.chat :
      item.category === "calendar" ? Ic.cal :
      item.category === "account" ? Ic.user : Ic.folder;
    return <Icon size={size} />;
  }
  return <Ic.mail size={size} />;
};

const ShCommRow = ({ tab, item, lang, checked, onCheck, onOpen, onToggleStar }) => {
  const title = tab === "letters" ? shCommLocal(lang, item.subject) : shCommLocal(lang, item.title);
  const secondary = tab === "letters"
    ? (item.folder === "sent" || item.folder === "draft" ? shCommLocal(lang, item.to) : shCommLocal(lang, item.from))
    : tab === "activities" ? shCommLocal(lang, item.actor) : shCommLocal(lang, item.meta);
  return (
    <article role="listitem" className={"comm-row" + (item.unread ? " unread" : "") + (checked ? " selected" : "")}>
      <label className="comm-check" title={shCommLocal(lang, SH_COMM_COPY.selected)}>
        <input type="checkbox" checked={checked} onChange={(event) => onCheck(item.id, event.target.checked)}
          aria-label={shCommLocal(lang, SH_COMM_COPY.selected) + ": " + title} />
        <span></span>
      </label>
      <button type="button" className="comm-row-main" onClick={() => onOpen(item.id)}>
        <span className={"comm-row-icon kind-" + (item.kind || item.category || item.folder)}>
          <ShCommStatusIcon tab={tab} item={item} size={17} />
        </span>
        <span className="comm-row-copy">
          <span className="comm-row-kicker">
            <span>{secondary}</span>
            {item.demo && <b className="comm-demo-tag">{shCommLocal(lang, SH_COMM_COPY.demo)}</b>}
            {item.priority === "high" && <b>{lang === "fa" ? "فوری" : lang === "ar" ? "عاجل" : "Urgent"}</b>}
          </span>
          <strong>{title}</strong>
          <small>{shCommLocal(lang, item.body)}</small>
          <span className="comm-row-time fa-num">{shCommLocal(lang, item.timeText)}</span>
        </span>
        {item.unread && <i className="comm-unread-dot" aria-label={shCommLocal(lang, SH_COMM_COPY.unread)}></i>}
      </button>
      <button type="button" className={"comm-star" + (item.starred ? " on" : "")}
        onClick={() => onToggleStar(item.id)}
        aria-pressed={item.starred}
        aria-label={shCommLocal(lang, item.starred ? SH_COMM_COPY.unstar : SH_COMM_COPY.star)}>
        <Ic.star size={15} />
      </button>
    </article>
  );
};

const ShCommComposer = ({ lang, initial, onClose, onSave }) => {
  const [draft, setDraft] = React.useState(() => ({
    id: initial?.id || null,
    to: initial?.to || "",
    subject: initial?.subject || "",
    body: initial?.body || "",
    attachments: Array.isArray(initial?.attachments) ? initial.attachments : []
  }));
  const [error, setError] = React.useState("");
  const [submitting, setSubmitting] = React.useState(false);
  const firstField = React.useRef(null);
  const dialogRef = React.useRef(null);
  const attachmentInput = React.useRef(null);
  const saveTimer = React.useRef(null);
  const submittingRef = React.useRef(false);
  const onCloseRef = React.useRef(onClose);

  React.useEffect(() => { submittingRef.current = submitting; }, [submitting]);
  React.useEffect(() => { onCloseRef.current = onClose; }, [onClose]);
  React.useEffect(() => {
    const previouslyFocused = document.activeElement;
    const focusTimer = window.setTimeout(() => firstField.current?.focus(), 30);
    const closeOnEscape = (event) => {
      if (event.key === "Escape") {
        event.preventDefault();
        event.stopImmediatePropagation();
        if (!submittingRef.current) onCloseRef.current();
        return;
      }
      if (event.key !== "Tab" || !dialogRef.current) return;
      const focusable = [...dialogRef.current.querySelectorAll(
        'button:not([disabled]), input:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
      )].filter((node) => !node.hasAttribute("hidden") && node.getAttribute("aria-hidden") !== "true" && node.getClientRects().length > 0);
      if (!focusable.length) {
        event.preventDefault();
        dialogRef.current.focus();
        return;
      }
      const first = focusable[0];
      const last = focusable[focusable.length - 1];
      if (event.shiftKey && document.activeElement === first) {
        event.preventDefault();
        last.focus();
      } else if (!event.shiftKey && document.activeElement === last) {
        event.preventDefault();
        first.focus();
      }
    };
    window.addEventListener("keydown", closeOnEscape, true);
    return () => {
      window.clearTimeout(focusTimer);
      window.clearTimeout(saveTimer.current);
      window.removeEventListener("keydown", closeOnEscape, true);
      if (previouslyFocused && previouslyFocused.isConnected) previouslyFocused.focus();
    };
  }, []);

  const save = (folder) => {
    if (folder === "sent" && (!draft.to.trim() || !draft.subject.trim() || !draft.body.trim())) {
      setError(lang === "fa" ? "گیرنده، موضوع و متن نامه برای ارسال لازم است." :
        lang === "ar" ? "المستلم والموضوع ونص الرسالة مطلوبة للإرسال." :
        "Recipient, subject and message are required to send.");
      firstField.current?.focus();
      return;
    }
    setError("");
    setSubmitting(true);
    saveTimer.current = window.setTimeout(() => {
      setSubmitting(false);
      onSave(folder, draft);
    }, 360);
  };

  return (
    <div className="comm-compose-layer" role="dialog" aria-modal="true"
      aria-labelledby="comm-compose-title" onMouseDown={(event) => {
        if (event.target === event.currentTarget && !submitting) onCloseRef.current();
      }}>
      <form ref={dialogRef} tabIndex="-1" className="comm-compose" onSubmit={(event) => { event.preventDefault(); save("sent"); }}>
        <header>
          <div>
            <span>{shCommLocal(lang, SH_COMM_COPY.localMode)}</span>
            <h3 id="comm-compose-title">{shCommLocal(lang, SH_COMM_COPY.compose)}</h3>
          </div>
          <button type="button" className="comm-icon-action" onClick={onClose} disabled={submitting}
            aria-label={lang === "fa" ? "بستن" : lang === "ar" ? "إغلاق" : "Close"}>
            <Ic.close size={17} />
          </button>
        </header>
        <div className="comm-compose-fields">
          <label>
            <span>{lang === "fa" ? "گیرنده" : lang === "ar" ? "المستلم" : "Recipient"}</span>
            <input ref={firstField} value={draft.to} maxLength={120} autoComplete="off"
              onChange={(event) => setDraft((current) => ({ ...current, to: event.target.value }))}
              placeholder={lang === "fa" ? "نام یا نشانی گیرنده" : lang === "ar" ? "اسم أو عنوان المستلم" : "Recipient name or address"} />
          </label>
          <label>
            <span>{lang === "fa" ? "موضوع" : lang === "ar" ? "الموضوع" : "Subject"}</span>
            <input value={draft.subject} maxLength={180}
              onChange={(event) => setDraft((current) => ({ ...current, subject: event.target.value }))}
              placeholder={lang === "fa" ? "موضوع کوتاه و روشن" : lang === "ar" ? "موضوع واضح ومختصر" : "A clear, concise subject"} />
          </label>
          <label className="comm-compose-body">
            <span>{lang === "fa" ? "متن نامه" : lang === "ar" ? "نص الرسالة" : "Message"}</span>
            <textarea value={draft.body} maxLength={12000}
              onChange={(event) => setDraft((current) => ({ ...current, body: event.target.value }))}
              placeholder={lang === "fa" ? "متن مکاتبه را بنویسید…" : lang === "ar" ? "اكتب نص الرسالة…" : "Write your message…"} />
            <small className="fa-num">{draft.body.length.toLocaleString(lang === "fa" ? "fa-IR" : lang === "ar" ? "ar" : "en-US")} / 12,000</small>
          </label>
          <div className="comm-attachment-control">
            <button type="button" className="comm-attachment-input" onClick={() => attachmentInput.current?.click()}>
              <Ic.paperclip size={16} />
              <span>{lang === "fa" ? "افزودن نام پیوست نمایشی" : lang === "ar" ? "إضافة اسم مرفق تجريبي" : "Add demo attachment name"}</span>
            </button>
            <input ref={attachmentInput} className="comm-attachment-file" type="file" multiple tabIndex="-1" onChange={(event) => {
              const names = Array.from(event.target.files || []).map((file) => file.name).slice(0, 8);
              setDraft((current) => ({ ...current, attachments: [...new Set([...current.attachments, ...names])].slice(0, 8) }));
              event.target.value = "";
            }} />
          </div>
          {draft.attachments.length > 0 &&
          <div className="comm-attachment-list">
            {draft.attachments.map((name) =>
              <button type="button" key={name} onClick={() => setDraft((current) => ({
                ...current, attachments: current.attachments.filter((fileName) => fileName !== name)
              }))}>
                <Ic.paperclip size={12} /><span>{name}</span><Ic.close size={11} />
              </button>
            )}
          </div>}
          {error && <p className="comm-form-error" role="alert">{error}</p>}
        </div>
        <footer>
          <div>
            <Ic.info size={14} />
            <span>{shCommLocal(lang, SH_COMM_COPY.privacyWarning)}</span>
          </div>
          <button type="button" className="comm-btn secondary" disabled={submitting} onClick={() => save("draft")}>
            {lang === "fa" ? "ذخیره پیش‌نویس" : lang === "ar" ? "حفظ المسودة" : "Save draft"}
          </button>
          <button type="submit" className="comm-btn primary" disabled={submitting}>
            {submitting
              ? (lang === "fa" ? "در حال ثبت…" : lang === "ar" ? "جارٍ الحفظ…" : "Saving…")
              : (lang === "fa" ? "ثبت در ارسال‌شده‌ها" : lang === "ar" ? "حفظ في المرسلة" : "Save to sent")}
            {!submitting && <Ic.send size={14} />}
          </button>
        </footer>
      </form>
    </div>
  );
};

const CommunicationsHub = ({ lang = "fa", session, onOpen }) => {
  const params = React.useMemo(() => new URLSearchParams(window.location.search), []);
  const requestedTab = params.get("tab");
  const initialTab = React.useMemo(() => (
    ["notifications", "activities", "letters"].includes(window.__shahdadCommInitialTab)
      ? window.__shahdadCommInitialTab
      : ["notifications", "activities", "letters"].includes(requestedTab) ? requestedTab : "notifications"
  ), [requestedTab]);

  const [state, updateState, persistenceOk] = useShahdadCommunications(session);
  const [tab, setTab] = React.useState(initialTab);
  const [clockNow, setClockNow] = React.useState(() => Date.now());
  const [filterByTab, setFilterByTab] = React.useState({
    notifications: "all",
    activities: "all",
    letters: "inbox"
  });
  const [query, setQuery] = React.useState("");
  const [selectedId, setSelectedId] = React.useState(() => params.get("item") || null);
  const [checkedIds, setCheckedIds] = React.useState([]);
  const [composer, setComposer] = React.useState(null);
  const [toast, setToast] = React.useState(null);
  const [undoState, setUndoState] = React.useState(null);
  const toastTimer = React.useRef(null);
  const tabRefs = React.useRef([]);

  const filter = filterByTab[tab];
  const filters = React.useMemo(() => shCommFilters(lang, tab), [lang, tab]);
  const visible = React.useMemo(
    () => shCommFilteredItems(state, tab, filter, query, lang, clockNow),
    [state, tab, filter, query, lang, clockNow]
  );
  const selected = React.useMemo(
    () => shCommCollection(state, tab).find((item) => item.id === selectedId) || null,
    [state, tab, selectedId]
  );
  const unreadNotifications = state.notifications.filter((item) =>
    item.unread && !item.archived && (!item.snoozedUntil || item.snoozedUntil <= clockNow)
  ).length;
  const unreadLetters = state.letters.filter((item) => item.folder === "inbox" && item.unread).length;
  const todayActivities = state.activities.filter((item) => clockNow - item.time < 24 * 60 * 60 * 1000 && !item.archived).length;

  React.useEffect(() => {
    window.__shahdadCommInitialTab = null;
    const clockTimer = window.setInterval(() => setClockNow(Date.now()), 60 * 1000);
    const navigate = (event) => {
      const nextTab = event?.detail?.tab;
      if (["notifications", "activities", "letters"].includes(nextTab)) {
        window.__shahdadCommInitialTab = null;
        setCheckedIds([]);
        setQuery("");
        setSelectedId(null);
        setTab(nextTab);
      }
    };
    window.addEventListener(SH_COMM_NAV_EVENT, navigate);
    return () => {
      window.clearInterval(clockTimer);
      window.clearTimeout(toastTimer.current);
      window.removeEventListener(SH_COMM_NAV_EVENT, navigate);
    };
  }, []);

  const showToast = (message, withUndo = false) => {
    window.clearTimeout(toastTimer.current);
    setToast({ message, withUndo });
    toastTimer.current = window.setTimeout(() => setToast(null), 4600);
  };

  const mutate = (message, updater, undoable = true) => {
    if (undoable) setUndoState(state);
    updateState(updater);
    showToast(
      shCommLastWriteSucceeded
        ? (message || shCommLocal(lang, SH_COMM_COPY.savedLocal))
        : shCommLocal(lang, SH_COMM_COPY.persistenceFailed),
      undoable
    );
  };

  const updateCollection = (current, ids, patcher) => {
    const key = tab === "notifications" ? "notifications" : tab === "activities" ? "activities" : "letters";
    const idSet = new Set(ids);
    return {
      ...current,
      [key]: current[key].map((item) => idSet.has(item.id) ? patcher(item) : item)
    };
  };

  const openItem = (id) => {
    setSelectedId(id);
    const item = shCommCollection(state, tab).find((candidate) => candidate.id === id);
    if (item && item.unread) {
      updateState((current) => updateCollection(current, [id], (candidate) => ({ ...candidate, unread: false })));
      if (!shCommLastWriteSucceeded) showToast(shCommLocal(lang, SH_COMM_COPY.persistenceFailed), false);
    }
  };

  const toggleChecked = (id, checked) => {
    setCheckedIds((current) => checked ? [...new Set([...current, id])] : current.filter((itemId) => itemId !== id));
  };

  const toggleStar = (id) => {
    mutate(shCommLocal(lang, SH_COMM_COPY.savedLocal),
      (current) => updateCollection(current, [id], (item) => ({ ...item, starred: !item.starred })));
  };

  const markItems = (ids, unread) => {
    mutate(shCommLocal(lang, SH_COMM_COPY.savedLocal),
      (current) => updateCollection(current, ids, (item) => ({ ...item, unread })));
    setCheckedIds([]);
  };

  const archiveItems = (ids, shouldArchive = true) => {
    mutate(shCommLocal(lang, SH_COMM_COPY.savedLocal),
      (current) => updateCollection(current, ids, (item) => tab === "letters"
        ? shouldArchive
          ? {
              ...item,
              previousFolder: item.folder === "archive" ? (item.previousFolder || "inbox") : item.folder,
              folder: "archive",
              archived: true
            }
          : {
              ...item,
              folder: item.previousFolder || "inbox",
              previousFolder: null,
              archived: false
            }
        : { ...item, archived: shouldArchive }));
    setCheckedIds([]);
    if (selectedId && ids.includes(selectedId)) setSelectedId(null);
  };

  const snoozeItem = (id) => {
    mutate(shCommLocal(lang, SH_COMM_COPY.savedLocal),
      (current) => updateCollection(current, [id], (item) => ({
        ...item, unread: true, snoozedUntil: Date.now() + 24 * 60 * 60 * 1000
      })));
    setSelectedId(null);
  };

  const markAllRead = () => {
    const ids = visible.filter((item) => item.unread).map((item) => item.id);
    if (!ids.length) return;
    markItems(ids, false);
  };

  const changeTab = (nextTab) => {
    AudioCtl.click();
    if (nextTab !== tab) {
      setCheckedIds([]);
      setQuery("");
      setSelectedId(null);
    }
    setTab(nextTab);
  };

  const onTabKeyDown = (event, index) => {
    const ids = tabItems.map((item) => item.id);
    let nextIndex = null;
    if (event.key === "Home") nextIndex = 0;
    if (event.key === "End") nextIndex = ids.length - 1;
    if (event.key === "ArrowRight") nextIndex = (index + (lang === "en" ? 1 : -1) + ids.length) % ids.length;
    if (event.key === "ArrowLeft") nextIndex = (index + (lang === "en" ? -1 : 1) + ids.length) % ids.length;
    if (nextIndex === null) return;
    event.preventDefault();
    changeTab(ids[nextIndex]);
    window.requestAnimationFrame(() => tabRefs.current[nextIndex]?.focus());
  };

  const openRelated = (item) => {
    if (!item?.action?.module) return;
    AudioCtl.open();
    if (typeof onOpen === "function") onOpen(item.action.module);
    else if (typeof window.__openModule === "function") window.__openModule(item.action.module);
  };

  const copyDeepLink = async (item) => {
    const url = new URL(window.location.href);
    url.searchParams.set("module", "communications");
    url.searchParams.set("tab", tab);
    url.searchParams.set("item", item.id);
    try {
      await navigator.clipboard.writeText(url.toString());
      showToast(shCommLocal(lang, SH_COMM_COPY.linkCopied), false);
    } catch {
      showToast(shCommLocal(lang, SH_COMM_COPY.linkCopyFailed), false);
    }
  };

  const startReply = (item, forward = false) => {
    const subject = shCommLocal(lang, item.subject);
    setComposer({
      to: forward ? "" : shCommLocal(lang, item.from),
      subject: `${forward ? (lang === "fa" ? "ارسال: " : "Fwd: ") : (lang === "fa" ? "پاسخ: " : "Re: ")}${subject}`,
      body: forward
        ? `\n\n----- ${lang === "fa" ? "متن نامه اصلی" : "Original message"} -----\n${shCommLocal(lang, item.body)}`
        : "",
      attachments: []
    });
  };

  const saveLetter = (folder, draft) => {
    const id = draft.id || `local-letter-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
    const nowLabel = {
      fa: folder === "draft" ? "پیش‌نویس همین حالا" : "ثبت‌شده همین حالا",
      en: folder === "draft" ? "Draft saved just now" : "Saved just now",
      ar: folder === "draft" ? "تم حفظ المسودة الآن" : "تم الحفظ الآن"
    };
    updateState((current) => {
      const letter = {
        id,
        folder,
        from: lang === "fa" ? "شما" : lang === "ar" ? "أنت" : "You",
        to: draft.to.trim(),
        subject: draft.subject.trim() || (lang === "fa" ? "بدون موضوع" : lang === "ar" ? "بدون موضوع" : "No subject"),
        body: draft.body.trim(),
        time: Date.now(),
        timeText: nowLabel,
        unread: false,
        starred: false,
        priority: "normal",
        archived: false,
        previousFolder: null,
        demo: false,
        attachments: draft.attachments
      };
      const exists = current.letters.some((item) => item.id === id);
      return {
        ...current,
        letters: exists
          ? current.letters.map((item) => item.id === id ? letter : item)
          : [letter, ...current.letters]
      };
    });
    setComposer(null);
    setTab("letters");
    setFilterByTab((current) => ({ ...current, letters: folder }));
    setSelectedId(id);
    showToast(!shCommLastWriteSucceeded
      ? shCommLocal(lang, SH_COMM_COPY.persistenceFailed)
      : folder === "draft"
        ? (lang === "fa" ? "پیش‌نویس روی این دستگاه ذخیره شد." : lang === "ar" ? "تم حفظ المسودة على هذا الجهاز." : "Draft saved on this device.")
        : (lang === "fa" ? "نامه در پوشه ارسال‌شده‌های محلی ثبت شد." : lang === "ar" ? "تم حفظ الرسالة في مجلد المرسلة المحلي." : "Letter saved to the local Sent folder."),
    false);
  };

  const undo = () => {
    if (!undoState) return;
    updateState(undoState);
    setUndoState(null);
    setToast(null);
  };

  const tabItems = [
    { id: "notifications", icon: Ic.bell, label: SH_COMM_COPY.notifications, count: unreadNotifications },
    { id: "activities", icon: Ic.clock, label: SH_COMM_COPY.activities, count: null },
    { id: "letters", icon: Ic.mail, label: SH_COMM_COPY.letters, count: unreadLetters }
  ];
  const isArchiveView = tab === "letters" ? filter === "archive" : filter === "archived";

  return (
    <div className="comm-hub">
      <header className="comm-hero" aria-hidden={composer ? "true" : undefined} inert={composer ? "" : undefined}>
        <div className="comm-hero-copy">
          <span><Ic.shieldCheck size={13} /> {shCommLocal(lang, SH_COMM_COPY.localMode)}</span>
          <h2>{shCommLocal(lang, SH_COMM_COPY.title)}</h2>
          <p>{shCommLocal(lang, SH_COMM_COPY.subtitle)}</p>
        </div>
        <div className="comm-summary" aria-label={lang === "fa" ? "خلاصه ارتباطات" : "Communications summary"}>
          <div><Ic.bell size={17} /><strong className="fa-num">{fmt(unreadNotifications, lang)}</strong><span>{shCommLocal(lang, SH_COMM_COPY.unread)}</span></div>
          <div><Ic.clock size={17} /><strong className="fa-num">{fmt(todayActivities, lang)}</strong><span>{shCommLocal(lang, SH_COMM_COPY.today)}</span></div>
          <div><Ic.mail size={17} /><strong className="fa-num">{fmt(unreadLetters, lang)}</strong><span>{shCommLocal(lang, SH_COMM_COPY.inbox)}</span></div>
        </div>
        <button type="button" className="comm-compose-trigger" onClick={() => setComposer({})}>
          <Ic.plus size={16} /><span>{shCommLocal(lang, SH_COMM_COPY.compose)}</span>
        </button>
      </header>

      <div className="comm-tabs-toolbar" aria-hidden={composer ? "true" : undefined} inert={composer ? "" : undefined}>
        <div className="comm-tabs" role="tablist" aria-label={shCommLocal(lang, SH_COMM_COPY.title)}>
          {tabItems.map(({ id, icon: Icon, label, count }, index) =>
            <button key={id} ref={(node) => { tabRefs.current[index] = node; }}
              id={`comm-tab-${id}`} type="button" role="tab" aria-selected={tab === id}
              aria-controls={`comm-panel-${id}`} tabIndex={tab === id ? 0 : -1}
              className={tab === id ? "active" : ""} onClick={() => changeTab(id)}
              onKeyDown={(event) => onTabKeyDown(event, index)}>
              <Icon size={16} /><span>{shCommLocal(lang, label)}</span>
              {count > 0 && <b className="fa-num">{fmt(count, lang)}</b>}
            </button>
          )}
        </div>
        <label className="comm-search">
          <Ic.search size={16} />
          <input value={query} onChange={(event) => setQuery(event.target.value)}
            aria-label={shCommLocal(lang, SH_COMM_COPY.search)}
            placeholder={shCommLocal(lang, SH_COMM_COPY.search)} />
          {query &&
          <button type="button" onClick={() => setQuery("")}
            aria-label={lang === "fa" ? "پاک کردن جست‌وجو" : lang === "ar" ? "مسح البحث" : "Clear search"}>
            <Ic.close size={13} />
          </button>}
        </label>
      </div>

      <div className="comm-shell" role="tabpanel" id={`comm-panel-${tab}`} aria-labelledby={`comm-tab-${tab}`}
        aria-hidden={composer ? "true" : undefined} inert={composer ? "" : undefined}>
        <aside className="comm-rail" aria-label={lang === "fa" ? "فیلترها" : "Filters"}>
          <div className="comm-rail-head">
            <span>{lang === "fa" ? "نمایش" : lang === "ar" ? "عرض" : "View"}</span>
            <b className="fa-num">{fmt(visible.length, lang)}</b>
          </div>
          <nav>
            {filters.map((entry) =>
              <button type="button" key={entry.id} className={filter === entry.id ? "active" : ""}
                onClick={() => {
                  setFilterByTab((current) => ({ ...current, [tab]: entry.id }));
                  setCheckedIds([]);
                  setSelectedId(null);
                }}>
                <span>{shCommLocal(lang, entry.label)}</span>
                {entry.id === "unread" && unreadNotifications > 0 && <b className="fa-num">{fmt(unreadNotifications, lang)}</b>}
                {entry.id === "inbox" && unreadLetters > 0 && <b className="fa-num">{fmt(unreadLetters, lang)}</b>}
              </button>
            )}
          </nav>
          <div className="comm-rail-note">
            <Ic.info size={14} />
            <p>{shCommLocal(lang, SH_COMM_COPY.privacyWarning)}</p>
          </div>
          {!persistenceOk &&
          <div className="comm-storage-warning" role="alert">
            <Ic.shieldAlert size={14} />
            <p>{shCommLocal(lang, SH_COMM_COPY.persistenceFailed)}</p>
          </div>}
        </aside>

        <section className={"comm-list-pane" + (checkedIds.length > 0 ? " has-bulk" : "") + (selected ? " is-hidden-mobile" : "")}
          aria-label={shCommLocal(lang, tabItems.find((entry) => entry.id === tab).label)}>
          <div className="comm-list-head">
            <label className="comm-select-all">
              <input type="checkbox"
                checked={visible.length > 0 && visible.every((item) => checkedIds.includes(item.id))}
                onChange={(event) => setCheckedIds(event.target.checked ? visible.map((item) => item.id) : [])} />
              <span>{shCommLocal(lang, SH_COMM_COPY.selectAll)}</span>
            </label>
            <button type="button" onClick={markAllRead} disabled={!visible.some((item) => item.unread)}>
              <Ic.shieldCheck size={14} />{shCommLocal(lang, SH_COMM_COPY.markAllRead)}
            </button>
          </div>

          {checkedIds.length > 0 &&
          <div className="comm-bulk" role="toolbar" aria-label={shCommLocal(lang, SH_COMM_COPY.selected)}>
            <strong className="fa-num">{fmt(checkedIds.length, lang)} {shCommLocal(lang, SH_COMM_COPY.selected)}</strong>
            <button type="button" onClick={() => markItems(checkedIds, false)}><Ic.shieldCheck size={13} />{shCommLocal(lang, SH_COMM_COPY.markRead)}</button>
            <button type="button" onClick={() => archiveItems(checkedIds, !isArchiveView)}>
              <Ic.folder size={13} />{shCommLocal(lang, isArchiveView ? SH_COMM_COPY.restore : SH_COMM_COPY.archive)}
            </button>
            <button type="button" onClick={() => setCheckedIds([])} aria-label={lang === "fa" ? "لغو انتخاب" : "Clear selection"}><Ic.close size={13} /></button>
          </div>}

          <div className="comm-list" role="list">
            {visible.map((item) =>
              <ShCommRow key={item.id} tab={tab} item={item} lang={lang}
                checked={checkedIds.includes(item.id)}
                onCheck={toggleChecked}
                onOpen={openItem}
                onToggleStar={toggleStar} />
            )}
            {!visible.length &&
            <div className="comm-empty">
              <span><Ic.search size={24} /></span>
              <strong>{shCommLocal(lang, SH_COMM_COPY.noResults)}</strong>
              <button type="button" onClick={() => {
                setQuery("");
                setFilterByTab((current) => ({ ...current, [tab]: tab === "letters" ? "inbox" : "all" }));
              }}>{lang === "fa" ? "پاک کردن فیلترها" : lang === "ar" ? "مسح المرشحات" : "Clear filters"}</button>
            </div>}
          </div>
        </section>

        <section className={"comm-detail-pane" + (selected ? " has-selection" : "")}
          aria-live="polite" aria-label={lang === "fa" ? "جزئیات" : "Details"}>
          {selected ?
          <React.Fragment>
            <div className="comm-detail-toolbar">
              <button type="button" className="comm-back-mobile" onClick={() => setSelectedId(null)}>
                <Ic.chevR size={14} />{shCommLocal(lang, SH_COMM_COPY.back)}
              </button>
              <div>
                <button type="button" className={selected.starred ? "on" : ""}
                  onClick={() => toggleStar(selected.id)}
                  title={shCommLocal(lang, selected.starred ? SH_COMM_COPY.unstar : SH_COMM_COPY.star)}
                  aria-label={shCommLocal(lang, selected.starred ? SH_COMM_COPY.unstar : SH_COMM_COPY.star)}>
                  <Ic.star size={15} />
                </button>
                {tab !== "activities" &&
                <button type="button" onClick={() => markItems([selected.id], !selected.unread)}
                  title={shCommLocal(lang, selected.unread ? SH_COMM_COPY.markRead : SH_COMM_COPY.markUnread)}
                  aria-label={shCommLocal(lang, selected.unread ? SH_COMM_COPY.markRead : SH_COMM_COPY.markUnread)}>
                  <Ic.shieldCheck size={15} />
                </button>}
                <button type="button" onClick={() => archiveItems([selected.id], !selected.archived)}
                  title={shCommLocal(lang, selected.archived ? SH_COMM_COPY.restore : SH_COMM_COPY.archive)}
                  aria-label={shCommLocal(lang, selected.archived ? SH_COMM_COPY.restore : SH_COMM_COPY.archive)}>
                  <Ic.folder size={15} />
                </button>
                <button type="button" onClick={() => copyDeepLink(selected)}
                  title={shCommLocal(lang, SH_COMM_COPY.copyLink)}
                  aria-label={shCommLocal(lang, SH_COMM_COPY.copyLink)}><Ic.paperclip size={15} /></button>
              </div>
            </div>
            <article className="comm-detail">
              <div className={"comm-detail-icon kind-" + (selected.kind || selected.category || selected.folder)}>
                <ShCommStatusIcon tab={tab} item={selected} size={22} />
              </div>
              <div className="comm-detail-meta">
                <span className="fa-num">{shCommLocal(lang, selected.timeText)}</span>
                {selected.demo && <b className="comm-demo-tag">{shCommLocal(lang, SH_COMM_COPY.demo)}</b>}
                {selected.priority === "high" && <b>{lang === "fa" ? "اولویت بالا" : lang === "ar" ? "أولوية عالية" : "High priority"}</b>}
                {selected.snoozedUntil > clockNow && <b>{lang === "fa" ? "تعویق‌خورده" : lang === "ar" ? "مؤجل" : "Snoozed"}</b>}
              </div>
              <h3>{tab === "letters" ? shCommLocal(lang, selected.subject) : shCommLocal(lang, selected.title)}</h3>
              {tab === "letters" &&
              <div className="comm-letter-address">
                <div><span>{lang === "fa" ? "از" : lang === "ar" ? "من" : "From"}</span><strong>{shCommLocal(lang, selected.from)}</strong></div>
                <div><span>{lang === "fa" ? "به" : lang === "ar" ? "إلى" : "To"}</span><strong>{shCommLocal(lang, selected.to)}</strong></div>
              </div>}
              {tab === "activities" && selected.actor &&
              <div className="comm-actor"><Ic.user size={14} /><span>{shCommLocal(lang, selected.actor)}</span></div>}
              <div className="comm-detail-body">{shCommLocal(lang, selected.body)}</div>

              {Array.isArray(selected.details) && selected.details.length > 0 &&
              <div className="comm-timeline">
                {selected.details.map((detail, index) =>
                  <div key={index}><i></i><span>{shCommLocal(lang, detail)}</span></div>
                )}
              </div>}

              {Array.isArray(selected.attachments) && selected.attachments.length > 0 &&
              <div className="comm-detail-attachments">
                <h4><Ic.paperclip size={14} />{lang === "fa" ? "پیوست‌ها" : lang === "ar" ? "المرفقات" : "Attachments"}</h4>
                {selected.attachments.map((name) =>
                  <div key={name}><Ic.doc size={15} /><span>{name}</span><small>{lang === "fa" ? "نمایشی" : lang === "ar" ? "تجريبي" : "Demo"}</small></div>
                )}
              </div>}

              <div className="comm-detail-actions">
                {selected.action?.module &&
                <button type="button" className="comm-btn primary" onClick={() => openRelated(selected)}>
                  {shCommLocal(lang, selected.action.label || SH_COMM_COPY.openRelated)}<Ic.chevL size={14} />
                </button>}
                {tab === "notifications" &&
                <button type="button" className="comm-btn secondary" onClick={() => snoozeItem(selected.id)}>
                  <Ic.clock size={14} />{shCommLocal(lang, SH_COMM_COPY.snooze)}
                </button>}
                {tab === "letters" && selected.folder !== "draft" &&
                <React.Fragment>
                  <button type="button" className="comm-btn primary" onClick={() => startReply(selected, false)}>
                    <Ic.mail size={14} />{shCommLocal(lang, SH_COMM_COPY.reply)}
                  </button>
                  <button type="button" className="comm-btn secondary" onClick={() => startReply(selected, true)}>
                    <Ic.send size={14} />{shCommLocal(lang, SH_COMM_COPY.forward)}
                  </button>
                </React.Fragment>}
                {tab === "letters" && selected.folder === "draft" &&
                <button type="button" className="comm-btn primary" onClick={() => setComposer({
                  id: selected.id,
                  to: shCommLocal(lang, selected.to),
                  subject: shCommLocal(lang, selected.subject),
                  body: shCommLocal(lang, selected.body),
                  attachments: selected.attachments || []
                })}>
                  <Ic.docEdit size={14} />{lang === "fa" ? "ادامه پیش‌نویس" : lang === "ar" ? "متابعة المسودة" : "Continue draft"}
                </button>}
              </div>
            </article>
          </React.Fragment> :
          <div className="comm-detail-empty">
            <span><Ic.mail size={28} /></span>
            <strong>{shCommLocal(lang, SH_COMM_COPY.chooseItem)}</strong>
            <p>{lang === "fa"
              ? "جزئیات، پیوست‌ها و اقدام‌های مرتبط اینجا نمایش داده می‌شوند."
              : lang === "ar"
              ? "تظهر هنا التفاصيل والمرفقات والإجراءات ذات الصلة."
              : "Details, attachments and related actions appear here."}</p>
          </div>}
        </section>
      </div>

      {composer &&
      <ShCommComposer lang={lang} initial={composer} onClose={() => setComposer(null)} onSave={saveLetter} />}

      {toast &&
      <div className="comm-toast" role="status" aria-live="polite">
        <Ic.shieldCheck size={15} />
        <span>{toast.message}</span>
        {toast.withUndo && undoState &&
        <button type="button" onClick={undo}>{shCommLocal(lang, SH_COMM_COPY.undo)}</button>}
        <button type="button" onClick={() => setToast(null)}
          aria-label={lang === "fa" ? "بستن پیام" : lang === "ar" ? "إغلاق الرسالة" : "Dismiss message"}>
          <Ic.close size={13} />
        </button>
      </div>}
    </div>
  );
};

window.ShahdadCommunications = {
  useStore: useShahdadCommunications,
  getSnapshot: shCommRead,
  getPreviewItems: shCommPreviewItems,
  localize: shCommLocal,
  setInitialTab: (tab) => {
    const nextTab = ["notifications", "activities", "letters"].includes(tab) ? tab : "notifications";
    window.__shahdadCommInitialTab = nextTab;
    try {
      window.dispatchEvent(new CustomEvent(SH_COMM_NAV_EVENT, { detail: { tab: nextTab } }));
    } catch {}
  }
};

if (window.MODULES) {
  window.MODULES.communications = {
    iconKey: "bell",
    t: { fa: "مرکز ارتباطات و پیگیری", en: "Communications Center", ar: "مركز الاتصالات والمتابعة" },
    s: { fa: "اعلان‌ها، فعالیت‌ها و نامه‌های شما", en: "Your alerts, activity and letters", ar: "تنبيهاتك وأنشطتك ورسائلك" },
    View: CommunicationsHub
  };
}

window.CommunicationsHub = CommunicationsHub;
