// src/pages/NewTicket.jsx
// ✅ FIX: attachmentFile => multipart/form-data (FormData)
// ✅ FIX: اگر فایل نبود => JSON
// ✅ Content-Type را دستی ست نمی‌کنیم (boundary)
// ✅ UI + محدودیت متن + Toast + RefreshGuard حفظ شد
// ✅ Audio Record: MediaRecorder
// ✅ FIX (این مشکل): فایل ضبط‌شده خالی/بی‌صدا
// ✅ NEW: محدودیت ضبط 5 دقیقه (300s)
// ✅ NEW: محدودیت حجم فایل 5MB (هم ضبط هم آپلود)

import React, { useEffect, useMemo, useRef, useState } from "react";
import { TbPaperclip, TbX, TbMicrophone, TbPlayerStop, TbTrash } from "react-icons/tb";
import { LuChevronDown, LuLink2, LuMinus, LuRefreshCcw, LuUpload, LuX } from "react-icons/lu";
import { Axiosinstance } from "../services/auth/Axiosinstance";
import { useToast } from "../components/ToastProvider";
import { Navigate, useNavigate } from "react-router";

const MAX_CHARS = 10000;

const MAX_RECORD_SECONDS = 5 * 60; // ✅ 5 دقیقه
const MIN_AUDIO_BYTES = 1200; // اگر کمتر از این بود احتمالاً خالیه
const MAX_UPLOAD_BYTES = 5 * 1024 * 1024; // ✅ 5MB

function cn(...a) {
  return a.filter(Boolean).join(" ");
}

const bytesToHuman = (bytes) => {
  if (!Number.isFinite(bytes)) return "-";
  const units = ["B", "KB", "MB", "GB", "TB"];
  let b = bytes;
  let i = 0;
  while (b >= 1024 && i < units.length - 1) {
    b /= 1024;
    i++;
  }
  return `${b.toFixed(i === 0 ? 0 : 2)} ${units[i]}`;
};

const extractItems = (data) => {
  const folders = Array.isArray(data?.folders) ? data.folders : [];
  const files = Array.isArray(data?.files) ? data.files : [];
  return { folders, files };
};

const flattenFilesFromTree = (folders, rootFiles = []) => {
  const out = [];

  (rootFiles || []).forEach((f) => {
    if (!f?.id) return;
    out.push({
      value: f.id,
      label: `/${f.name || "بدون نام"}`,
      meta: f,
    });
  });

  const walk = (node, prefix) => {
    const name = node?.name || "بدون نام";
    const path = `${prefix}/${name}`;

    (node?.files || []).forEach((f) => {
      if (!f?.id) return;
      out.push({
        value: f.id,
        label: `${path}/${f.name || "بدون نام"}`,
        meta: f,
      });
    });

    (node?.folders || []).forEach((child) => walk(child, path));
  };

  (folders || []).forEach((f) => walk(f, ""));

  return out.sort((a, b) => a.label.localeCompare(b.label, "fa"));
};

const getErrMsg = (e, fallback = "خطا") => {
  const d = e?.response?.data;
  if (!d) return e?.message || fallback;
  if (typeof d === "string") return d;
  if (d.detail) return d.detail;

  const keys = ["subject", "body", "priority", "related_file_id", "attachment"];
  for (const k of keys) {
    if (Array.isArray(d?.[k]) && d[k][0]) return d[k][0];
  }
  try {
    return JSON.stringify(d);
  } catch {
    return fallback;
  }
};

/* ---------------- Dropdown Like Navbar ---------------- */
function DropdownLikeNavbar({ label, value, onChange, options = [], placeholder = "انتخاب کنید", widthClass = "w-full" }) {
  const [open, setOpen] = useState(false);
  const wrapRef = useRef(null);
  const selected = options.find((o) => String(o.value) === String(value));

  useEffect(() => {
    const onDocDown = (e) => {
      if (!wrapRef.current) return;
      if (!wrapRef.current.contains(e.target)) setOpen(false);
    };
    const onEsc = (e) => e.key === "Escape" && setOpen(false);
    document.addEventListener("mousedown", onDocDown);
    document.addEventListener("keydown", onEsc);
    return () => {
      document.removeEventListener("mousedown", onDocDown);
      document.removeEventListener("keydown", onEsc);
    };
  }, []);

  return (
    <div className={`${widthClass} grid gap-3`}>
      <p className="font-medium text-sm text-black dark:text-white">{label}</p>

      <div ref={wrapRef} className="relative">
        <button
          type="button"
          onClick={() => setOpen((p) => !p)}
          aria-haspopup="menu"
          aria-expanded={open}
          className={cn(
            widthClass,
            "border-l-12 text-sm border-transparent",
            "bg-[#A1E3F980] dark:bg-[#A1E3F910]",
            "rounded-xl shadow py-3 px-4 outline-0 ring-0",
            "flex items-center justify-between gap-3",
            "text-black dark:text-white"
          )}
        >
          <span className={cn(selected ? "" : "text-black/50 dark:text-white/60", "truncate")}>{selected ? selected.label : placeholder}</span>
          <LuChevronDown className={cn("w-4 h-4 transition text-black/60 dark:text-white/70", open ? "rotate-180" : "")} />
        </button>

        <div
          className={cn(
            "absolute top-full mt-3 z-[9999] left-0 right-0",
            "rounded-xl border border-white/10 bg-[#f2f2f2] dark:bg-[#0b2445]",
            "shadow-lg overflow-hidden origin-top-right transition-all duration-200",
            open ? "opacity-100 translate-y-0 pointer-events-auto" : "opacity-0 -translate-y-2 pointer-events-none"
          )}
          role="menu"
        >
          <div className="p-2 max-h-[320px] overflow-y-auto">
            {options.length === 0 ? (
              <div className="px-3 py-2 text-sm text-black/50 dark:text-white/60">گزینه‌ای موجود نیست</div>
            ) : (
              options.map((opt) => {
                const active = String(opt.value) === String(value);
                return (
                  <button
                    key={opt.value}
                    type="button"
                    onClick={() => {
                      onChange(opt.value);
                      setOpen(false);
                    }}
                    className={cn(
                      "w-full text-right text-sm px-3 py-2 flex gap-2 items-center rounded-xl",
                      active ? "bg-[#43BEFF40] dark:bg-[#A1E3F919]" : "hover:bg-[#43BEFF40] dark:hover:bg-[#A1E3F919]",
                      "text-black dark:text-white"
                    )}
                    title={opt.label}
                  >
                    <span className="truncate">{opt.label}</span>
                  </button>
                );
              })
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

/* ---------------- Audio Recorder helpers ---------------- */
function pickBestRecorderMime() {
  const MR = window.MediaRecorder;
  if (!MR?.isTypeSupported) return { mimeType: "", ext: "webm" };

  const list = [
    { mt: "audio/webm;codecs=opus", ext: "webm" },
    { mt: "audio/webm", ext: "webm" },
    { mt: "audio/ogg;codecs=opus", ext: "ogg" },
    { mt: "audio/ogg", ext: "ogg" },
  ];

  for (const it of list) {
    try {
      if (MR.isTypeSupported(it.mt)) return { mimeType: it.mt, ext: it.ext };
    } catch {}
  }
  return { mimeType: "", ext: "webm" };
}

function formatTime(sec) {
  const s = Math.max(0, Math.floor(sec));
  const mm = String(Math.floor(s / 60)).padStart(2, "0");
  const ss = String(s % 60).padStart(2, "0");
  return `${mm}:${ss}`;
}

/* ---------------- Drag & Drop Upload + Record ---------------- */
function DropzoneWithRecorder({
  file,
  audioUrl,
  isRecording,
  recordSec,
  canRecord,
  disabled,

  onPick,
  onClear,

  onStartRecording,
  onStopRecording,
  onDiscardRecording,
}) {
  const inputRef = useRef(null);
  const [drag, setDrag] = useState(false);

  const openPicker = () => {
    if (disabled) return;
    inputRef.current?.click();
  };

  const showRecorded = !!audioUrl && !file;

  return (
    <div className="grid gap-3">
      <div className="flex items-center justify-between flex-wrap gap-2">
        <p className="font-medium text-sm text-black dark:text-white">پیوست (اختیاری)</p>

        <div className="flex items-center gap-2 flex-wrap">
          <button
            type="button"
            disabled={disabled || !canRecord || isRecording}
            onClick={onStartRecording}
            className={cn(
              "px-3 py-2 rounded-xl text-xs font-bold border border-black/10 dark:border-white/10",
              "bg-white/70 dark:bg-white/10 hover:bg-black/5 dark:hover:bg-white/10 transition flex items-center gap-2",
              disabled || !canRecord || isRecording ? "opacity-60 cursor-not-allowed" : ""
            )}
            title={!canRecord ? "مرورگر شما ضبط صدا را پشتیبانی نمی‌کند" : "شروع ضبط صدا"}
          >
            <TbMicrophone className="w-4 h-4" />
            ضبط صدا
          </button>

          <button
            type="button"
            disabled={disabled || !isRecording}
            onClick={onStopRecording}
            className={cn(
              "px-3 py-2 rounded-xl text-xs font-bold border border-black/10 dark:border-white/10",
              "bg-white/70 dark:bg-white/10 hover:bg-red-500/10 transition flex items-center gap-2",
              disabled || !isRecording ? "opacity-60 cursor-not-allowed" : ""
            )}
            title="توقف ضبط"
          >
            <TbPlayerStop className="w-4 h-4 text-red-600 dark:text-red-300" />
            توقف
          </button>

          <button
            type="button"
            disabled={disabled || (!audioUrl && !file) || isRecording}
            onClick={() => {
              if (file) onClear?.();
              else onDiscardRecording?.();
            }}
            className={cn(
              "px-3 py-2 rounded-xl text-xs font-bold border border-black/10 dark:border-white/10",
              "bg-white/70 dark:bg-white/10 hover:bg-red-500/10 transition flex items-center gap-2",
              disabled || (!audioUrl && !file) || isRecording ? "opacity-60 cursor-not-allowed" : ""
            )}
            title="حذف پیوست"
          >
            <TbTrash className="w-4 h-4 text-red-600 dark:text-red-300" />
            حذف
          </button>

          {isRecording && (
            <span className="text-xs font-bold text-red-600 dark:text-red-300 inline-flex items-center gap-2">
              <span className="w-2 h-2 rounded-full bg-red-500 animate-pulse" />
              در حال ضبط: {formatTime(recordSec)}
              <span className="text-black/60 dark:text-white/60 font-normal">(حداکثر {formatTime(MAX_RECORD_SECONDS)})</span>
            </span>
          )}
        </div>
      </div>

      <input ref={inputRef} type="file" accept="image/*,.png,.jpg,.jpeg,.webp,.pdf,audio/*,.mp3,.wav,.m4a,.webm,.ogg" className="hidden" onChange={(e) => onPick?.(e.target.files?.[0] || null)} />

      <div
        role="button"
        tabIndex={0}
        onClick={openPicker}
        onKeyDown={(e) => (e.key === "Enter" ? openPicker() : null)}
        onDragOver={(e) => {
          e.preventDefault();
          if (!disabled) setDrag(true);
        }}
        onDragLeave={(e) => {
          e.preventDefault();
          setDrag(false);
        }}
        onDrop={(e) => {
          e.preventDefault();
          setDrag(false);
          if (disabled) return;
          const f = e.dataTransfer.files?.[0];
          if (f) onPick?.(f);
        }}
        className={cn(
          "rounded-2xl border-2 border-dashed p-4 sm:p-6 cursor-pointer transition",
          "bg-[#A1E3F919] dark:bg-[#A1E3F910]",
          drag ? "border-[#43BEFF] shadow-[0_0_0_6px_rgba(67,190,255,0.15)]" : "border-black/10 dark:border-white/10",
          disabled ? "opacity-60 cursor-not-allowed" : ""
        )}
      >
        {!!file && (
          <div className="flex items-center justify-between gap-4">
            <div className="min-w-0">
              <div className="text-sm font-extrabold text-black dark:text-white truncate">{file.name}</div>
              <div className="mt-1 text-xs text-black/60 dark:text-white/70">
                حجم: <b className="text-black dark:text-white">{bytesToHuman(file.size)}</b>
              </div>
              <div className="mt-1 text-[11px] text-black/50 dark:text-white/60">
                type: <b className="text-black dark:text-white">{file.type || "-"}</b>
              </div>
            </div>

            <button
              type="button"
              onClick={(e) => {
                e.stopPropagation();
                onClear?.();
                if (inputRef.current) inputRef.current.value = "";
              }}
              className="px-3 py-2 rounded-xl text-xs font-bold border border-black/10 dark:border-white/10 bg-white/70 dark:bg-white/10 hover:bg-red-500/10 transition flex items-center gap-2"
              title="حذف فایل"
            >
              <TbX className="w-4 h-4 text-red-600 dark:text-red-300" />
              حذف
            </button>
          </div>
        )}

        {!file && showRecorded && (
          <div className="grid gap-3">
            <div className="flex items-center justify-between gap-3 flex-wrap">
              <div className="min-w-0">
                <div className="text-sm font-extrabold text-black dark:text-white">صدای ضبط‌شده آماده‌ی ارسال ✅</div>
                <div className="mt-1 text-xs text-black/60 dark:text-white/70">برای حذف، دکمه «حذف» را بزن.</div>
              </div>

              <span className="px-2 py-1 rounded-lg text-[11px] font-bold bg-black/5 dark:bg-white/10 text-black/70 dark:text-white/80">طول: {formatTime(recordSec)}</span>
            </div>

            <audio controls src={audioUrl} className="w-full" />
          </div>
        )}

        {!file && !showRecorded && (
          <div className="flex items-center justify-between gap-4">
            <div className="min-w-0">
              <div className="text-sm font-extrabold text-black dark:text-white">Drag & Drop</div>
              <div className="mt-1 text-xs text-black/60 dark:text-white/70">فایل را اینجا رها کنید یا برای انتخاب کلیک کنید (یا از «ضبط صدا» استفاده کن)</div>
              <div className="mt-2 text-[11px] text-black/50 dark:text-white/60">
                محدودیت حجم فایل: <b className="text-black dark:text-white">۵ مگابایت</b>
              </div>
            </div>

            <div className="w-10 h-10 rounded-2xl bg-white/60 dark:bg-white/10 border border-black/10 dark:border-white/10 flex items-center justify-center">
              <TbPaperclip className="w-5 h-5 text-black/70 dark:text-white/80" />
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

/* ---------------- Main ---------------- */
function NewTicket() {
  const navigate = useNavigate();
  const { success, error: toastError, info, warning } = useToast();

  const editorRef = useRef(null);
  const limitToastLockRef = useRef(false);

  const [subject, setSubject] = useState("");
  const [priority, setPriority] = useState("normal");
  const [relatedFileId, setRelatedFileId] = useState("");

  const [attachmentUrl, setAttachmentUrl] = useState("");
  const [attachmentFile, setAttachmentFile] = useState(null);

  // --- audio record states ---
  const canRecord = typeof window !== "undefined" && !!navigator.mediaDevices?.getUserMedia && !!window.MediaRecorder;
  const mediaStreamRef = useRef(null);
  const mediaRecorderRef = useRef(null);
  const chunksRef = useRef([]);
  const recordTickRef = useRef(null);
  const flushRef = useRef(null); // ✅ requestData loop
  const stopRequestedRef = useRef(false);

  const [isRecording, setIsRecording] = useState(false);
  const [recordSec, setRecordSec] = useState(0);
  const [recordedUrl, setRecordedUrl] = useState("");

  const [isHtmlMode, setIsHtmlMode] = useState(false);
  const [htmlValue, setHtmlValue] = useState("");

  const [submitting, setSubmitting] = useState(false);

  const [filesLoading, setFilesLoading] = useState(false);
  const [fileOptions, setFileOptions] = useState([]);
  const [fileSearch, setFileSearch] = useState("");

  /* ==================== Refresh Guard ==================== */
  const REFRESH_COOLDOWN_MS = 3000;
  const refreshInFlightRef = useRef(false);
  const refreshLastAtRef = useRef(0);
  const refreshToastLockRef = useRef(0);
  const [cooldownLeftMs, setCooldownLeftMs] = useState(0);

  useEffect(() => {
    if (cooldownLeftMs <= 0) return;
    const t = setInterval(() => {
      const left = Math.max(0, REFRESH_COOLDOWN_MS - (Date.now() - refreshLastAtRef.current));
      setCooldownLeftMs(left);
      if (left <= 0) clearInterval(t);
    }, 120);
    return () => clearInterval(t);
  }, [cooldownLeftMs]);
  /* ======================================================= */

  const syncFromEditor = () => {
    if (!editorRef.current) return;
    setHtmlValue(editorRef.current.innerHTML);
  };

  const exec = (command, value = null) => {
    editorRef.current?.focus();
    document.execCommand(command, false, value);
    syncFromEditor();
  };

  const toggleHtmlMode = () => {
    const ed = editorRef.current;
    if (!ed) return;

    if (!isHtmlMode) {
      setHtmlValue(ed.innerHTML);
      setIsHtmlMode(true);
      return;
    }

    ed.innerHTML = htmlValue;
    setIsHtmlMode(false);
    requestAnimationFrame(() => ed.focus());
  };

  const addLink = () => {
    const url = window.prompt("لینک را وارد کنید (مثلاً https://example.com):");
    if (!url) return;
    exec("createLink", url);
  };

  const validate = () => {
    if (!subject.trim()) return "موضوع تیکت را وارد کنید.";
    const txt = editorRef.current?.innerText?.trim() || "";
    if (!txt) return "متن تیکت را وارد کنید.";
    if (!["low", "normal", "high", "urgent"].includes(priority)) return "اولویت نامعتبر است.";
    if (relatedFileId && String(relatedFileId).trim().length < 10) return "فایل مرتبط نامعتبر است.";
    if (txt.length > MAX_CHARS) return `متن تیکت نباید بیشتر از ${MAX_CHARS} کاراکتر باشد.`;

    // ✅ محدودیت حجم فایل
    if (attachmentFile && attachmentFile.size > MAX_UPLOAD_BYTES) {
      return "حجم پیوست بیشتر از ۵ مگابایت است. فایل کوچکتر انتخاب کن.";
    }

    return "";
  };

  const placeCaretAtEnd = (el) => {
    try {
      el.focus();
      const range = document.createRange();
      range.selectNodeContents(el);
      range.collapse(false);
      const sel = window.getSelection();
      sel.removeAllRanges();
      sel.addRange(range);
    } catch {}
  };

  const enforceEditorLimit = (silent = false) => {
    const el = editorRef.current;
    if (!el) return false;

    const txt = el.innerText || "";
    if (txt.length <= MAX_CHARS) return false;

    el.innerText = txt.slice(0, MAX_CHARS);
    placeCaretAtEnd(el);
    setHtmlValue(el.innerHTML);

    if (!silent && !limitToastLockRef.current) {
      limitToastLockRef.current = true;
      warning(`حداکثر ${MAX_CHARS} کاراکتر مجاز است.`, { title: "محدودیت متن" });
      setTimeout(() => (limitToastLockRef.current = false), 800);
    }
    return true;
  };

  const fetchStorageItems = async ({ silent = false } = {}) => {
    setFilesLoading(true);
    try {
      const res = await Axiosinstance.get("storage/items/");
      const { folders, files } = extractItems(res?.data || {});
      const opts = flattenFilesFromTree(folders, files);
      setFileOptions(opts);
      if (!silent) info("لیست فایل‌ها بروزرسانی شد.", { title: "فایل‌های مرتبط" });
    } catch (e) {
      setFileOptions([]);
      if (!silent) toastError(getErrMsg(e, "خطا در دریافت فایل‌ها"), { title: "فایل‌های مرتبط" });
    } finally {
      setFilesLoading(false);
    }
  };

  const guardedFetchStorageItems = async () => {
    const now = Date.now();

    if (refreshInFlightRef.current || filesLoading) {
      if (now - refreshToastLockRef.current > 900) {
        refreshToastLockRef.current = now;
        info("در حال بروزرسانی فایل‌ها...", { title: "فایل‌های مرتبط" });
      }
      return;
    }

    const diff = now - refreshLastAtRef.current;
    if (diff < REFRESH_COOLDOWN_MS) {
      const left = REFRESH_COOLDOWN_MS - diff;
      setCooldownLeftMs(left);

      if (now - refreshToastLockRef.current > 900) {
        refreshToastLockRef.current = now;
        info(`لطفاً ${Math.ceil(left / 1000)} ثانیه صبر کن.`, { title: "بروزرسانی" });
      }
      return;
    }

    refreshInFlightRef.current = true;
    refreshLastAtRef.current = now;
    setCooldownLeftMs(REFRESH_COOLDOWN_MS);

    try {
      await fetchStorageItems({ silent: false });
    } finally {
      refreshInFlightRef.current = false;
    }
  };

  useEffect(() => {
    fetchStorageItems({ silent: true });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const filteredOptions = useMemo(() => {
    const q = fileSearch.trim().toLowerCase();
    if (!q) return fileOptions;
    return fileOptions.filter((o) => (o.label || "").toLowerCase().includes(q));
  }, [fileOptions, fileSearch]);

  // ---------- recorder cleanups ----------
  const stopTick = () => {
    if (recordTickRef.current) {
      clearInterval(recordTickRef.current);
      recordTickRef.current = null;
    }
  };

  const stopFlush = () => {
    if (flushRef.current) {
      clearInterval(flushRef.current);
      flushRef.current = null;
    }
  };

  const cleanupStreamOnly = () => {
    if (mediaStreamRef.current) {
      try {
        mediaStreamRef.current.getTracks?.().forEach((t) => t.stop());
      } catch {}
    }
    mediaStreamRef.current = null;
  };

  const discardRecording = () => {
    stopTick();
    stopFlush();
    stopRequestedRef.current = false;

    try {
      const mr = mediaRecorderRef.current;
      if (mr && mr.state !== "inactive") {
        try {
          mr.stop();
        } catch {}
      }
    } catch {}

    mediaRecorderRef.current = null;
    chunksRef.current = [];

    cleanupStreamOnly();

    setIsRecording(false);
    setRecordSec(0);

    if (recordedUrl) URL.revokeObjectURL(recordedUrl);
    setRecordedUrl("");
  };

  useEffect(() => {
    return () => {
      discardRecording();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // ✅ helper: ست کردن فایل فقط اگر <= 5MB
  const setAttachmentWithLimit = (file, { title = "پیوست" } = {}) => {
    if (!file) {
      setAttachmentFile(null);
      return true;
    }
    if (file.size > MAX_UPLOAD_BYTES) {
      warning(`حجم فایل بیشتر از ۵ مگابایت است (${bytesToHuman(file.size)}).`, { title });
      return false;
    }
    setAttachmentFile(file);
    return true;
  };

  // ✅ Start Recording (با flush loop + محدودیت 5 دقیقه)
  const startRecording = async () => {
    if (!canRecord) {
      warning("مرورگر شما امکان ضبط صدا را پشتیبانی نمی‌کند.", { title: "ضبط صدا" });
      return;
    }
    if (submitting || isRecording) return;

    // پاک کردن فایل/ضبط قبلی
    if (attachmentFile) setAttachmentFile(null);
    if (recordedUrl) {
      URL.revokeObjectURL(recordedUrl);
      setRecordedUrl("");
    }

    stopRequestedRef.current = false;
    chunksRef.current = [];

    try {
      const stream = await navigator.mediaDevices.getUserMedia({
        audio: {
          echoCancellation: true,
          noiseSuppression: true,
          autoGainControl: true,
        },
      });
      mediaStreamRef.current = stream;

      const { mimeType, ext } = pickBestRecorderMime();

      const mr = new MediaRecorder(stream, mimeType ? { mimeType, audioBitsPerSecond: 128000 } : { audioBitsPerSecond: 128000 });

      mediaRecorderRef.current = mr;
      setRecordSec(0);
      setIsRecording(true);

      mr.ondataavailable = (ev) => {
        if (ev.data && ev.data.size > 0) chunksRef.current.push(ev.data);
      };

      mr.onerror = (ev) => {
        toastError("خطا در ضبط صدا", { title: "ضبط صدا" });
        console.log("MediaRecorder error:", ev);
      };

      mr.onstop = async () => {
        stopTick();
        stopFlush();
        setIsRecording(false);

        await new Promise((r) => setTimeout(r, 120));

        const chunks = chunksRef.current || [];
        chunksRef.current = [];

        const typeGuess = mr.mimeType || chunks.find((c) => c?.type)?.type || (ext === "ogg" ? "audio/ogg" : "audio/webm");

        const blob = new Blob(chunks, { type: typeGuess });

        if (!blob || blob.size < MIN_AUDIO_BYTES) {
          cleanupStreamOnly();
          warning("فایل ضبط‌شده خالی بود یا صدا ضبط نشد. دوباره ضبط کن و مطمئن شو میکروفون فعال است.", { title: "ضبط صدا" });
          return;
        }

        // ✅ محدودیت ۵ مگ برای فایل ضبط‌شده
        if (blob.size > MAX_UPLOAD_BYTES) {
          cleanupStreamOnly();
          warning(`حجم صدای ضبط‌شده بیشتر از ۵ مگابایت شد (${bytesToHuman(blob.size)}).`, { title: "محدودیت حجم" });
          // ضبط را نگه نمی‌داریم
          if (recordedUrl) URL.revokeObjectURL(recordedUrl);
          setRecordedUrl("");
          setAttachmentFile(null);
          return;
        }

        const url = URL.createObjectURL(blob);
        setRecordedUrl(url);

        const safeExt = ext || (typeGuess.includes("ogg") ? "ogg" : "webm");
        const fileName = `voice-${new Date().toISOString().replace(/[:.]/g, "-")}.${safeExt}`;
        const f = new File([blob], fileName, { type: blob.type || typeGuess });

        // دوباره چک
        if (!setAttachmentWithLimit(f, { title: "محدودیت حجم" })) {
          if (url) URL.revokeObjectURL(url);
          setRecordedUrl("");
          cleanupStreamOnly();
          return;
        }

        success("صدای ضبط‌شده به پیوست اضافه شد ✅", { title: "ضبط صدا" });
        cleanupStreamOnly();
      };

      // ✅ start بدون timeslice + flush loop
      mr.start();

      // هر ~800ms داده را flush کن
      flushRef.current = setInterval(() => {
        try {
          const rec = mediaRecorderRef.current;
          if (!rec || rec.state !== "recording") return;
          rec.requestData?.();
        } catch {}
      }, 800);

      // ✅ تایمر + محدودیت ۵ دقیقه
      recordTickRef.current = setInterval(() => {
        setRecordSec((s) => {
          const next = s + 1;

          // نزدیک پایان: یه toast کوچک
          if (next === MAX_RECORD_SECONDS - 10) {
            info("۱۰ ثانیه تا پایان ضبط باقی مانده.", { title: "ضبط صدا" });
          }

          if (next >= MAX_RECORD_SECONDS) {
            try {
              info("حداکثر زمان ضبط (۵ دقیقه) رسید. ضبط متوقف شد.", { title: "ضبط صدا" });
              stopRecording();
            } catch {}
          }
          return next;
        });
      }, 1000);

      info("ضبط شروع شد. حداکثر ۵ دقیقه.", { title: "ضبط صدا" });
    } catch (e) {
      discardRecording();
      toastError(getErrMsg(e, "اجازه میکروفون داده نشد یا خطا رخ داد"), { title: "ضبط صدا" });
    }
  };

  // ✅ Stop Recording (با requestData قبل stop)
  const stopRecording = () => {
    if (!isRecording) return;

    const mr = mediaRecorderRef.current;
    if (!mr) return;

    if (stopRequestedRef.current) return;
    stopRequestedRef.current = true;

    try {
      try {
        mr.requestData?.();
      } catch {}

      setTimeout(() => {
        try {
          if (mr.state !== "inactive") mr.stop();
        } catch {
          discardRecording();
        }
      }, 150);
    } catch {
      discardRecording();
    }
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    enforceEditorLimit(true);

    const err = validate();
    if (err) {
      warning(err, { title: "تکمیل فرم" });
      return;
    }

    if (isRecording) {
      warning("اول ضبط صدا را متوقف کن، بعد ارسال بزن.", { title: "ضبط صدا" });
      return;
    }

    const text = editorRef.current?.innerText?.trim() || "";
    const hasFile = !!attachmentFile;
    const hasUrl = !!attachmentUrl.trim();

    setSubmitting(true);
    try {
      if (hasFile) {
        // ✅ دوباره چک حجم قبل ارسال
        if (attachmentFile.size > MAX_UPLOAD_BYTES) {
          warning("حجم پیوست بیشتر از ۵ مگابایت است. ارسال انجام نشد.", { title: "محدودیت حجم" });
          setSubmitting(false);
          return;
        }

        const fd = new FormData();
        fd.append("subject", subject.trim());
        fd.append("body", text);
        fd.append("priority", priority);
        if (relatedFileId) fd.append("related_file_id", String(relatedFileId).trim());
        fd.append("attachment", attachmentFile);

        await Axiosinstance.post("tickets/", fd);
      } else {
        const payload = {
          subject: subject.trim(),
          body: text,
          priority,
          related_file_id: relatedFileId ? String(relatedFileId).trim() : undefined,
          attachment: hasUrl ? attachmentUrl.trim() : undefined,
        };
        Object.keys(payload).forEach((k) => payload[k] === undefined && delete payload[k]);
        await Axiosinstance.post("tickets/", payload);
      }

      success("تیکت با موفقیت ثبت شد ✅", { title: "تیکت" });

      // reset
      setSubject("");
      setPriority("normal");
      setRelatedFileId("");
      setAttachmentUrl("");
      setAttachmentFile(null);

      discardRecording();

      setIsHtmlMode(false);
      setHtmlValue("");
      navigate("/myticket");
      if (editorRef.current) editorRef.current.innerHTML = "";
    } catch (e2) {
      toastError(getErrMsg(e2, "خطا در ثبت تیکت"), { title: "ثبت تیکت" });
      console.log("ticket create error:", e2?.response?.data || e2);
    } finally {
      setSubmitting(false);
    }
  };

  const refreshDisabled = filesLoading || cooldownLeftMs > 0;
  const refreshLabel = filesLoading ? "درحال بروزرسانی..." : "بروزرسانی فایل‌ها";

  return (
    <div className="dark:text-white w-full max-w-7xl">
      <div className="mb-5 rounded-2xl border border-black/10 dark:border-white/10 bg-[#A1E3F910] shadow dark:bg-[#A1E3F910] p-3 sm:p-4">
        <div className="flex flex-col lg:flex-row gap-3 lg:items-center lg:justify-between min-w-0">
          <div className="min-w-0">
            <h1 className="text-lg sm:text-xl font-extrabold">ثبت تیکت جدید</h1>
            <p className="text-xs sm:text-sm text-black/60 dark:text-white/70 truncate">موضوع و متن را وارد کن و ارسال بزن.</p>
          </div>

          <div className="flex items-center gap-2 w-full lg:w-auto">
            <button
              type="button"
              onClick={guardedFetchStorageItems}
              disabled={refreshDisabled}
              className="px-3 py-2 rounded-xl shadow bg-white dark:bg-[#16325b] border border-black/10 dark:border-white/10 hover:bg-black/5 dark:hover:bg-white/10 text-sm inline-flex items-center gap-2 disabled:opacity-60"
              title={cooldownLeftMs > 0 ? `تا ${Math.ceil(cooldownLeftMs / 1000)} ثانیه دیگر` : "بروزرسانی لیست فایل‌ها"}
            >
              <LuRefreshCcw className={cn("w-4 h-4 text-[#43BEFF] dark:text-white", filesLoading ? "animate-spin" : "")} />
              {refreshLabel}
            </button>
          </div>
        </div>
      </div>

      <form onSubmit={handleSubmit} className="grid gap-6">
        <div className="flex flex-wrap md:flex-nowrap items-start gap-6">
          <div className="md:w-1/2 w-full">
            <DropdownLikeNavbar
              label="اولویت تیکت"
              value={priority}
              onChange={setPriority}
              placeholder="انتخاب اولویت"
              options={[
                { value: "low", label: "کم" },
                { value: "normal", label: "عادی" },
                { value: "high", label: "زیاد" },
                { value: "urgent", label: "فوری" },
              ]}
            />
          </div>

          <div className="md:w-1/2 w-full grid gap-3">
            <DropdownLikeNavbar
              label="انتخاب فایل مرتبط (اختیاری)"
              value={relatedFileId}
              onChange={setRelatedFileId}
              placeholder={filesLoading ? "در حال دریافت..." : "انتخاب فایل"}
              options={filteredOptions}
            />
          </div>
        </div>

        <div className="sm:border dark:border-white/10 border-[#A1E3F9] grid gap-6 rounded-xl shadow sm:p-6">
          <div className="w-full bg-[#A1E3F980] dark:bg-[#A1E3F910] rounded-xl shadow grid relative items-center">
            <input
              className="peer h-12 px-4 w-full outline-0 focus:outline-0 rounded-xl shadow placeholder:hidden bg-transparent text-black dark:text-white"
              placeholder=" "
              type="text"
              id="ticket-subject"
              value={subject}
              onChange={(e) => setSubject(e.target.value)}
              disabled={submitting}
            />
            <label
              htmlFor="ticket-subject"
              className="absolute px-4 duration-200 text-sm font-medium pointer-events-none
                peer-not-placeholder-shown:text-sm peer-not-placeholder-shown:px-3 peer-not-placeholder-shown:-translate-y-6
                peer-focus:text-sm peer-focus:px-3 peer-focus:-translate-y-6"
            >
              موضوع تیکت
            </label>
          </div>

          <div className="w-full bg-[#A1E3F980] dark:bg-[#A1E3F910] rounded-xl shadow overflow-hidden">
            <div className="p-2 border-b dark:border-white/10 border-white flex flex-wrap items-center gap-2">
              <button
                type="button"
                onMouseDown={(e) => e.preventDefault()}
                onClick={() => exec("bold")}
                className="px-2 min-w-8 py-1 text-sm rounded dark:hover:bg-white/10 dark:border dark:bg-transparent dark:border-white/10 bg-white border-0"
                title="Bold"
                disabled={submitting}
              >
                <b>B</b>
              </button>

              <button
                type="button"
                onMouseDown={(e) => e.preventDefault()}
                onClick={() => exec("italic")}
                className="px-2 min-w-8 py-1 text-sm rounded dark:hover:bg-white/10 dark:border dark:bg-transparent dark:border-white/10 bg-white border-0"
                title="Italic"
                disabled={submitting}
              >
                <i>I</i>
              </button>

              <button
                type="button"
                onMouseDown={(e) => e.preventDefault()}
                onClick={() => exec("underline")}
                className="px-2 min-w-8 py-1 text-sm rounded dark:hover:bg-white/10 dark:border dark:bg-transparent dark:border-white/10 bg-white border-0"
                title="Underline"
                disabled={submitting}
              >
                <u>U</u>
              </button>

              <div className="mx-1 w-px h-5 bg-white dark:bg-white/10" />

              <button
                type="button"
                onMouseDown={(e) => e.preventDefault()}
                onClick={addLink}
                className="px-2 min-w-8 py-1 text-sm rounded dark:hover:bg-white/10 dark:border dark:bg-transparent dark:border-white/10 bg-white border-0"
                title="Add link"
                disabled={submitting}
              >
                <LuLink2 />
              </button>

              <button
                type="button"
                onMouseDown={(e) => e.preventDefault()}
                onClick={() => exec("unlink")}
                className="px-2 min-w-8 py-1 text-sm rounded dark:hover:bg-white/10 dark:border dark:bg-transparent dark:border-white/10 bg-white border-0"
                title="Remove link"
                disabled={submitting}
              >
                <LuX />
              </button>

              <div className="mx-1 w-px h-5 bg-white dark:bg-white/10" />

              <button
                type="button"
                onMouseDown={(e) => e.preventDefault()}
                onClick={() => exec("insertHorizontalRule")}
                className="px-2 min-w-8 py-1 text-sm rounded dark:hover:bg-white/10 dark:border dark:bg-transparent dark:border-white/10 bg-white border-0"
                title="Divider"
                disabled={submitting}
              >
                <LuMinus />
              </button>

              <div className="mx-1 w-px h-5 bg-white dark:bg-white/10" />

              <button
                type="button"
                onMouseDown={(e) => e.preventDefault()}
                onClick={toggleHtmlMode}
                className="px-2 min-w-8 py-1 text-sm rounded dark:hover:bg-white/10 dark:border dark:bg-transparent dark:border-white/10 bg-white border-0 font-mono"
                title="Switch HTML/Editor"
                disabled={submitting}
              >
                {isHtmlMode ? "Editor" : "</> HTML"}
              </button>
            </div>

            <div className="p-4 bg-white dark:bg-[#A1E3F910]">
              <div
                ref={editorRef}
                contentEditable={!isHtmlMode}
                suppressContentEditableWarning
                onInput={() => {
                  syncFromEditor();
                  enforceEditorLimit(false);
                }}
                onDrop={(e) => e.preventDefault()}
                onPaste={(e) => {
                  e.preventDefault();
                  const pasteText = e.clipboardData.getData("text/plain");
                  const el = editorRef.current;
                  if (!el) return;

                  const current = el.innerText || "";
                  const available = MAX_CHARS - current.length;

                  if (available <= 0) {
                    if (!limitToastLockRef.current) {
                      limitToastLockRef.current = true;
                      warning(`حداکثر ${MAX_CHARS} کاراکتر مجاز است.`, { title: "محدودیت متن" });
                      setTimeout(() => (limitToastLockRef.current = false), 800);
                    }
                    return;
                  }

                  const toInsert = pasteText.slice(0, available);
                  el.focus();
                  document.execCommand("insertText", false, toInsert);
                  syncFromEditor();
                }}
                className={cn(
                  "min-h-[200px] w-full max-w-full box-border overflow-hidden whitespace-pre-wrap break-all break-words text-sm outline-none text-black dark:text-white",
                  isHtmlMode ? "hidden" : "block"
                )}
                style={{ whiteSpace: "pre-wrap", overflowWrap: "anywhere", wordBreak: "break-word" }}
              />

              {isHtmlMode && (
                <textarea
                  value={htmlValue}
                  onChange={(e) => setHtmlValue(e.target.value.slice(0, MAX_CHARS))}
                  className="w-full min-h-[200px] bg-black/30 p-3 text-xs font-mono outline-none rounded text-white"
                  spellCheck={false}
                  maxLength={MAX_CHARS}
                  disabled={submitting}
                />
              )}
            </div>
          </div>

          <DropzoneWithRecorder
            file={attachmentFile}
            audioUrl={recordedUrl}
            isRecording={isRecording}
            recordSec={recordSec}
            canRecord={canRecord}
            disabled={submitting}
            onPick={(f) => {
              // ✅ محدودیت 5MB برای آپلود دستی
              if (!f) {
                setAttachmentFile(null);
                return;
              }
              if (f.size > MAX_UPLOAD_BYTES) {
                warning(`حجم فایل بیشتر از ۵ مگابایت است (${bytesToHuman(f.size)}).`, { title: "محدودیت حجم" });
                return;
              }

              if (recordedUrl) {
                URL.revokeObjectURL(recordedUrl);
                setRecordedUrl("");
              }
              setAttachmentFile(f);
              info("پیوست انتخاب شد.", { title: "پیوست" });
            }}
            onClear={() => {
              setAttachmentFile(null);
              info("پیوست حذف شد.", { title: "پیوست" });
            }}
            onStartRecording={startRecording}
            onStopRecording={stopRecording}
            onDiscardRecording={() => {
              discardRecording();
              setAttachmentFile(null);
              info("ضبط حذف شد.", { title: "ضبط صدا" });
            }}
          />

          <div className="flex items-center justify-end gap-3">
            <button
              type="submit"
              disabled={submitting || isRecording}
              className="px-4 py-2 rounded-xl shadow dark:bg-[#001F3F] bg-[#f2f2f2] hover:bg-[#e2e2e2] dark:hover:bg-[#A1E3F919] text-sm disabled:opacity-60 flex items-center gap-2"
              title={isRecording ? "اول ضبط را متوقف کن" : "ارسال تیکت"}
            >
              <LuUpload className="w-4 h-4" />
              {submitting ? "درحال ثبت..." : "ارسال تیکت"}
            </button>
          </div>
        </div>
      </form>
    </div>
  );
}

export default NewTicket;
