// Generic Modal + Toast utilities
const { useState: useStateUI, useEffect: useEffectUI } = React;

window.Modal = function Modal({ open, onClose, title, subtitle, children, footer, width = 520, closeOnBackdrop = false }) {
  useEffectUI(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === "Escape") onClose && onClose(); };
    window.addEventListener("keydown", onKey);
    document.body.style.overflow = "hidden";
    return () => {
      window.removeEventListener("keydown", onKey);
      document.body.style.overflow = "";
    };
  }, [open, onClose]);

  if (!open) return null;
  const Icons = window.Icons;
  return (
    <div className="modal-backdrop" onClick={(e) => { if (closeOnBackdrop && e.target === e.currentTarget) onClose && onClose(); }}>
      <div className="modal" style={{ width }} role="dialog" aria-modal="true">
        <div className="modal-head">
          <div>
            <h3 className="modal-title">{title}</h3>
            {subtitle && <p className="modal-sub">{subtitle}</p>}
          </div>
          <button className="icon-btn" onClick={onClose} aria-label="Close"><Icons.X /></button>
        </div>
        <div className="modal-body">{children}</div>
        {footer && <div className="modal-foot">{footer}</div>}
      </div>
    </div>
  );
};

// Toast (singleton, mounts at root)
const ToastContext = React.createContext(null);
window.ToastProvider = function ToastProvider({ children }) {
  const [toasts, setToasts] = useStateUI([]);
  const push = (msg, kind = "info") => {
    const id = Math.random().toString(36).slice(2, 8);
    setToasts(t => [...t, { id, msg, kind }]);
    setTimeout(() => setToasts(t => t.filter(x => x.id !== id)), 3200);
  };
  return (
    <ToastContext.Provider value={push}>
      {children}
      <div className="toast-stack" aria-live="polite">
        {toasts.map(t => (
          <div key={t.id} className={`toast toast-${t.kind}`}>
            <span>{t.msg}</span>
          </div>
        ))}
      </div>
    </ToastContext.Provider>
  );
};
window.useToast = () => React.useContext(ToastContext) || (() => {});

// Popover (anchored to trigger)
window.Popover = function Popover({ open, onClose, anchorRef, children, width = 240, align = "right" }) {
  const [pos, setPos] = useStateUI({ top: 0, left: 0 });
  useEffectUI(() => {
    if (!open || !anchorRef?.current) return;
    const r = anchorRef.current.getBoundingClientRect();
    setPos({
      top: r.bottom + 6,
      left: align === "right" ? r.right - width : r.left,
    });
    const onDoc = (e) => {
      if (anchorRef.current?.contains(e.target)) return;
      onClose && onClose();
    };
    setTimeout(() => document.addEventListener("mousedown", onDoc), 0);
    return () => document.removeEventListener("mousedown", onDoc);
  }, [open, anchorRef, width, align]);

  if (!open) return null;
  return (
    <div
      className="ui-popover"
      style={{ top: pos.top, left: pos.left, width }}
      onClick={(e) => e.stopPropagation()}
      onMouseDown={(e) => e.stopPropagation()}
    >
      {children}
    </div>
  );
};

// Custom Select — drop-in replacement for native <select>
window.Select = function Select({ value, onChange, options, placeholder, disabled, width, align = "left" }) {
  const [open, setOpen] = useStateUI(false);
  const [trigW, setTrigW] = useStateUI(undefined);
  const ref = React.useRef(null);
  const Icons = window.Icons;
  const normalized = (options || []).map(o => typeof o === "string" || typeof o === "number" ? { value: o, label: String(o) } : o);
  const current = normalized.find(o => String(o.value) === String(value));

  const toggle = () => {
    if (disabled) return;
    if (!open && ref.current) setTrigW(ref.current.offsetWidth);
    setOpen(o => !o);
  };

  return (
    <>
      <button
        ref={ref}
        type="button"
        className="input select-trigger"
        data-open={open}
        onClick={toggle}
        disabled={disabled}
        style={{ width }}
      >
        <span className={current ? "select-value" : "select-placeholder"}>
          {current ? current.label : (placeholder || "Seleccionar...")}
        </span>
        <Icons.ChevronDown className="select-chevron" />
      </button>
      <window.Popover open={open} onClose={() => setOpen(false)} anchorRef={ref} width={trigW || 200} align={align}>
        <div className="ui-popover-section" style={{ maxHeight: 280, overflowY: "auto" }}>
          {normalized.map(o => {
            const active = String(o.value) === String(value);
            return (
              <button
                key={o.value}
                type="button"
                className="ui-popover-item"
                data-active={active}
                onClick={() => { onChange(o.value); setOpen(false); }}
              >
                <span style={{ flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{o.label}</span>
                {active && <Icons.Check className="select-check" />}
              </button>
            );
          })}
        </div>
      </window.Popover>
    </>
  );
};

// Date helpers
function _parseISO(s) {
  if (!s) return null;
  const [y, m, d] = String(s).split("-").map(Number);
  if (!y || !m || !d) return null;
  return new Date(y, m - 1, d);
}
function _toISO(d) {
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, "0");
  const dd = String(d.getDate()).padStart(2, "0");
  return `${y}-${m}-${dd}`;
}
function _sameDay(a, b) {
  return !!a && !!b && a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
}

const _MONTHS = {
  es: ["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"],
  en: ["January","February","March","April","May","June","July","August","September","October","November","December"],
};
const _DOW = {
  es: ["L","M","X","J","V","S","D"],
  en: ["M","T","W","T","F","S","S"],
};

function _getLang() {
  return (window.__TWEAK_DEFAULTS?.language) || "es";
}

window.DatePicker = function DatePicker({ value, onChange, placeholder, disabled, clearable = true }) {
  const [open, setOpen] = useStateUI(false);
  const ref = React.useRef(null);
  const Icons = window.Icons;
  const parsed = _parseISO(value);
  const initialView = parsed || new Date();
  const [view, setView] = useStateUI(new Date(initialView.getFullYear(), initialView.getMonth(), 1));

  useEffectUI(() => {
    if (open) {
      const anchor = parsed || new Date();
      setView(new Date(anchor.getFullYear(), anchor.getMonth(), 1));
    }
  }, [open]);

  const lang = _getLang();
  const months = _MONTHS[lang] || _MONTHS.es;
  const dows = _DOW[lang] || _DOW.es;

  const display = parsed
    ? `${String(parsed.getDate()).padStart(2, "0")}/${String(parsed.getMonth() + 1).padStart(2, "0")}/${parsed.getFullYear()}`
    : (placeholder || (lang === "en" ? "Select date" : "Seleccionar fecha"));

  const select = (date) => { onChange(_toISO(date)); setOpen(false); };
  const clear = () => { onChange(""); setOpen(false); };

  const year = view.getFullYear();
  const month = view.getMonth();
  const firstDow = (new Date(year, month, 1).getDay() + 6) % 7; // Mon=0
  const daysInMonth = new Date(year, month + 1, 0).getDate();
  const daysInPrev = new Date(year, month, 0).getDate();
  const today = new Date();

  const cells = [];
  for (let i = firstDow - 1; i >= 0; i--) {
    const d = daysInPrev - i;
    cells.push({ date: new Date(year, month - 1, d), other: true });
  }
  for (let d = 1; d <= daysInMonth; d++) {
    cells.push({ date: new Date(year, month, d), other: false });
  }
  while (cells.length < 42) {
    const d = cells.length - firstDow - daysInMonth + 1;
    cells.push({ date: new Date(year, month + 1, d), other: true });
  }

  return (
    <>
      <button
        ref={ref}
        type="button"
        className="input select-trigger"
        data-open={open}
        onClick={() => !disabled && setOpen(o => !o)}
        disabled={disabled}
      >
        <span className={parsed ? "select-value" : "select-placeholder"}>{display}</span>
        <Icons.Calendar className="select-chevron" />
      </button>
      <window.Popover open={open} onClose={() => setOpen(false)} anchorRef={ref} width={272} align="left">
        <div className="date-panel">
          <div className="date-panel-head">
            <button type="button" className="date-nav" onClick={() => setView(new Date(year, month - 1, 1))} aria-label="Previous month">
              <Icons.ChevronLeft />
            </button>
            <span className="date-panel-title">{months[month]} {year}</span>
            <button type="button" className="date-nav" onClick={() => setView(new Date(year, month + 1, 1))} aria-label="Next month">
              <Icons.ChevronRight />
            </button>
          </div>
          <div className="date-grid">
            {dows.map((d, i) => <div key={`dow-${i}`} className="date-dow">{d}</div>)}
            {cells.map((c, i) => (
              <button
                key={i}
                type="button"
                className="date-cell"
                data-other={c.other || undefined}
                data-today={_sameDay(c.date, today) || undefined}
                data-selected={_sameDay(c.date, parsed) || undefined}
                onClick={() => select(c.date)}
              >
                {c.date.getDate()}
              </button>
            ))}
          </div>
          <div className="date-panel-foot">
            {clearable && parsed
              ? <button type="button" className="date-foot-btn" onClick={clear}>{lang === "en" ? "Clear" : "Limpiar"}</button>
              : <span />}
            <button type="button" className="date-foot-btn brand" onClick={() => select(today)}>{lang === "en" ? "Today" : "Hoy"}</button>
          </div>
        </div>
      </window.Popover>
    </>
  );
};

// HTML5 drag-and-drop niceties — call from onDragStart with the dragged DOM
// element. We do two things the native API can't do well:
//
//   1) Suppress the browser's translucent drag image (it always renders ~50%
//      transparent, washed out) by passing a 1×1 empty canvas to setDragImage.
//   2) Render OUR OWN clone as a position:fixed floating element that tracks
//      the cursor on the global `dragover` event. Fully opaque, full control.
//
// The source itself is hidden (visibility: hidden via .dragging-source CSS) so
// it really feels like you've picked the card up — there's no "ghost" left
// behind, just the floating clone in your hand.
//
//   onDragStart={(e) => { e.dataTransfer.setData(...); window.startDrag(e, e.currentTarget); }}
//
// Options:
//   - flat: true  → suppress the playful rotation (good for long table rows).
window.startDrag = function startDrag(event, sourceEl, options = {}) {
  if (!sourceEl || !event.dataTransfer) return;
  const rect = sourceEl.getBoundingClientRect();

  // 1×1 empty canvas as the native drag image — the browser still draws a tiny
  // dot of nothing, but that's invisible and doesn't conflict with our own clone.
  const empty = document.createElement("canvas");
  empty.width = empty.height = 1;
  try { event.dataTransfer.setDragImage(empty, 0, 0); } catch (_) { /* unsupported */ }

  // Where the cursor sits within the source — we keep this offset constant so
  // the floating clone stays "glued" to the same point of the card.
  const offsetX = Math.max(0, Math.min(rect.width,  event.clientX - rect.left));
  const offsetY = Math.max(0, Math.min(rect.height, event.clientY - rect.top));

  // <tr> outside a <table> renders broken (no cells, no width). When the source
  // is a row, wrap the clone in a synthetic table that copies the original's
  // class for styling and the original cells' widths for layout fidelity.
  const sourceIsRow = sourceEl.tagName === "TR";
  let cloneRoot;
  if (sourceIsRow) {
    const rowClone = sourceEl.cloneNode(true);
    const origCells = sourceEl.children;
    Array.from(rowClone.children).forEach((td, i) => {
      const orig = origCells[i];
      if (orig) td.style.width = orig.getBoundingClientRect().width + "px";
    });
    const tbl = document.createElement("table");
    const origTbl = sourceEl.closest("table");
    if (origTbl) tbl.className = origTbl.className;
    tbl.style.tableLayout = "fixed";
    tbl.style.borderCollapse = "collapse";
    const tbody = document.createElement("tbody");
    tbody.appendChild(rowClone);
    tbl.appendChild(tbody);
    cloneRoot = tbl;
  } else {
    cloneRoot = sourceEl.cloneNode(true);
  }

  cloneRoot.classList.add("dnd-floating");
  if (options.flat) cloneRoot.classList.add("flat");
  cloneRoot.style.position = "fixed";
  cloneRoot.style.left = rect.left + "px";
  cloneRoot.style.top  = rect.top  + "px";
  cloneRoot.style.width = rect.width + "px";
  cloneRoot.style.margin = "0";
  cloneRoot.style.pointerEvents = "none";
  cloneRoot.style.zIndex = "10000";
  document.body.appendChild(cloneRoot);

  const onMove = (e) => {
    // Some browsers fire a final dragover with (0,0) when the pointer leaves
    // the window. Ignore those so the clone doesn't snap to the corner.
    if (e.clientX === 0 && e.clientY === 0) return;
    cloneRoot.style.left = (e.clientX - offsetX) + "px";
    cloneRoot.style.top  = (e.clientY - offsetY) + "px";
  };
  document.addEventListener("dragover", onMove);

  // Defer the source-hide by a tick so the dragstart event has fully resolved.
  setTimeout(() => {
    document.body.classList.add("is-dragging");
    sourceEl.classList.add("dragging-source");
  }, 0);

  const cleanup = () => {
    document.body.classList.remove("is-dragging");
    sourceEl.classList.remove("dragging-source");
    document.removeEventListener("dragover", onMove);
    sourceEl.removeEventListener("dragend", cleanup);
    document.removeEventListener("drop", cleanup, true);
    cloneRoot.remove();
  };
  sourceEl.addEventListener("dragend", cleanup);
  // Safety net: if dragend is suppressed after a drop outside the source's
  // subtree, the document-level drop still tears the clone down.
  document.addEventListener("drop", cleanup, true);
};

// Helpers
window.downloadCSV = function downloadCSV(filename, rows) {
  const csv = rows.map(r => r.map(v => {
    const s = String(v ?? "");
    return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
  }).join(",")).join("\n");
  const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
  const a = document.createElement("a");
  a.href = URL.createObjectURL(blob);
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  a.remove();
};

Object.assign(window, { Modal: window.Modal, ToastProvider: window.ToastProvider, useToast: window.useToast, Popover: window.Popover, Select: window.Select, DatePicker: window.DatePicker, downloadCSV: window.downloadCSV });
