/* global React, I */
// =============== Shared UI Components ===============

const { useState, useEffect, useRef, useMemo } = React;

// Money formatter (Thai Baht)
const fmt = (n, opts = {}) => {
  if (n === null || n === undefined || isNaN(n)) return "—";
  const { dec = 0, sign = false } = opts;
  const s = Math.abs(n).toLocaleString("th-TH", { minimumFractionDigits: dec, maximumFractionDigits: dec });
  return (sign ? (n > 0 ? "+" : n < 0 ? "−" : "") : (n < 0 ? "−" : "")) + s;
};
const fmtM = (n) => fmt(n, { dec: 2 });
const fmt0 = (n) => fmt(n, { dec: 0 });

// Convert numbers to Thai bath text (simple version)
function bahtText(num) {
  const txt = ["", "หนึ่ง", "สอง", "สาม", "สี่", "ห้า", "หก", "เจ็ด", "แปด", "เก้า"];
  const places = ["", "สิบ", "ร้อย", "พัน", "หมื่น", "แสน", "ล้าน"];
  function group(n) {
    let s = ""; let str = String(Math.floor(n));
    for (let i = 0; i < str.length; i++) {
      const d = parseInt(str[str.length - 1 - i], 10);
      const place = i % 6;
      if (d === 0) continue;
      if (place === 0 && i > 0 && d === 1) s = "เอ็ด" + s;
      else if (place === 1 && d === 2) s = "ยี่" + places[place] + s;
      else if (place === 1 && d === 1) s = places[place] + s;
      else s = txt[d] + places[place] + s;
    }
    return s || "ศูนย์";
  }
  const baht = Math.floor(num);
  const satang = Math.round((num - baht) * 100);
  let result = group(baht) + "บาท";
  result += satang === 0 ? "ถ้วน" : group(satang) + "สตางค์";
  return result;
}

// Extract a readable label from button children/props (for the fallback toast)
function btnLabel(children, rest) {
  const fromNode = (c) => {
    if (c === null || c === undefined || typeof c === "boolean") return "";
    if (typeof c === "string") return c.trim();
    if (typeof c === "number") return String(c);
    if (Array.isArray(c)) { for (const x of c) { const s = fromNode(x); if (s) return s; } return ""; }
    if (typeof c === "object" && c.props) return fromNode(c.props.children); // React element (incl. editor __om-t wrapper)
    return "";
  };
  return fromNode(children) || rest.title || rest["aria-label"] || "";
}

// Button — falls back to a toast when no onClick is supplied, so no button is ever inert.
const Btn = ({ kind = "default", size = "", icon, children, onClick, ...rest }) => {
  const klass = `btn ${kind === "primary" ? "primary" : kind === "ghost" ? "ghost" : kind === "danger" ? "danger" : ""} ${size}`;
  const handleClick = onClick || ((e) => {
    if (e && e.stopPropagation) e.stopPropagation();
    const label = btnLabel(children, rest);
    if (window.toast) window.toast(label || "ดำเนินการแล้ว");
  });
  // Only forward valid DOM attributes — never leak component-only props (e.g. icon) onto <button>
  const domProps = {};
  for (const k in rest) {
    if (/^(on[A-Z]|aria-|data-)/.test(k) || ["id", "style", "className", "title", "type", "disabled", "tabIndex", "role", "name", "value", "form"].includes(k)) {
      domProps[k] = rest[k];
    }
  }
  return (
    <button className={klass.trim()} onClick={handleClick} {...domProps}>
      {icon ? React.createElement(icon, { className: "icon" }) : null}
      {children}
    </button>
  );
};

// Badge
const statusMap = {
  "ชำระแล้ว": "success", "อนุมัติแล้ว": "success", "ส่งแล้ว": "success", "จ่ายแล้ว": "success",
  "ยื่นแล้ว": "success", "รับสินค้าแล้ว": "success", "เก็บเงินแล้ว": "success", "ส่งงาน": "success",
  "ปกติ": "success", "ใช้งานอยู่": "success",
  "รอยื่น": "warning", "รออนุมัติ": "warning", "ร่าง": "outline",
  "ชำระบางส่วน": "warning", "กำลังจัดส่ง": "warning", "กำลังผลิต": "warning",
  "ออกแบบ 3D": "info", "ตรวจสอบคุณภาพ": "info", "ลูกค้านัดรับ": "info",
  "เกินกำหนด": "danger", "ปฏิเสธ": "danger", "ค้างชำระ": "warning",
  "OT": "info", "สาย": "warning", "ทดลองงาน": "info",
  "แปลงเป็นใบสั่ง": "info", "ออกเช็คแล้ว": "info", "ทำงาน": "success",
  "ลูกค้ามารับเอง": "outline", "รอเก็บเงิน": "warning",
  "เบิกใช้งาน": "warning", "รับเข้า": "success", "ปรับสต็อก": "info", "ขาย": "success",
  "รอเคลียร์": "warning", "เคลียร์แล้ว": "success", "เกินกำหนดเคลียร์": "danger",
  "ยกเลิกแล้ว": "danger", "ยกเลิก": "danger", "รอจ่าย": "warning", "รออนุมัติจ่าย": "warning",
  "เต็มรูป": "info", "อย่างย่อ": "info", "ขาดงาน": "danger", "ลา": "info", "ลาเช้า": "info", "ลาบ่าย": "info",
  "ออกแล้ว": "success", "หักแล้ว": "info", "รับเข้าแล้ว": "success", "เปิดงาน": "info", "เสร็จสิ้น": "success", "หยุด": "outline"
};
const Badge = ({ children, tone, dot = false }) => {
  const t = tone || statusMap[children] || "";
  return <span className={`badge ${t}`}>{dot && <span className="dot"/>}{children}</span>;
};

// Stat tile
const STAT_COLORS = ["var(--c-blue)", "var(--c-violet)", "var(--c-cyan)", "var(--c-amber)", "var(--c-lime)", "var(--c-pink)"];
function statColor(label) { let h = 0; const s = String(label || ""); for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0; return STAT_COLORS[h % STAT_COLORS.length]; }
const Stat = ({ label, value, delta, deltaDir, sub, icon, color }) => {
  const c = color || statColor(label);
  return (
    <div className="stat" style={{ "--stat-c": c }}>
      <div className="label">
        {icon ? <span className="stat-ico" style={{ color: c, background: `color-mix(in oklab, ${c} 15%, transparent)` }}>{React.createElement(icon, { size: 15 })}</span> : null}
        {label}
      </div>
      <div className="value">{value}</div>
      {(delta || sub) && (
        <div className={`delta ${deltaDir || ""}`}>
          {deltaDir === "up" && <I.arrowUp size={12}/>}
          {deltaDir === "down" && <I.arrowDown size={12}/>}
          <span>{delta || sub}</span>
        </div>
      )}
    </div>
  );
};

// Card
const Card = ({ title, sub, right, children, flush = false, padded = true, className = "" }) => (
  <div className={`card ${className}`}>
    {(title || right) && (
      <div className="card-head">
        {title && <h3>{title}</h3>}
        {sub && <span className="sub">{sub}</span>}
        {right && <div className="right">{right}</div>}
      </div>
    )}
    <div className={`card-body ${flush ? "flush" : ""}`} style={padded ? {} : { padding: 0 }}>{children}</div>
  </div>
);

// Page header
const PageHead = ({ title, sub, right }) => (
  <div className="page-head">
    <div>
      <h2>{title}</h2>
      {sub && <div className="sub">{sub}</div>}
    </div>
    {right && <div className="right">{right}</div>}
  </div>
);

// Tabs
const Tabs = ({ items, active, onChange }) => (
  <div className="tabs">
    {items.map(it => (
      <button key={it.key} className={`tab ${active === it.key ? "active" : ""}`} onClick={() => onChange(it.key)}>
        {it.label}
        {it.count !== undefined && <span style={{marginLeft: 6, color: "var(--ink-3)"}}>{it.count}</span>}
      </button>
    ))}
  </div>
);

// Drawer
const Drawer = ({ open, onClose, title, children, footer, wide = false, xwide = false }) => {
  useEffect(() => {
    if (!open) return;
    const onKey = (e) => e.key === "Escape" && onClose && onClose();
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [open, onClose]);
  if (!open) return null;
  const node = (
    <>
      <div className="scrim" onClick={onClose}/>
      <div className={`drawer ${xwide ? "xwide" : (wide ? "wide" : "")}`}>
        <div className="drawer-head">
          <h3>{title}</h3>
          <span className="spacer"/>
          <button className="icon-btn" onClick={onClose}><I.close size={14}/></button>
        </div>
        <div className="drawer-body">{children}</div>
        {footer && <div className="drawer-foot">{footer}</div>}
      </div>
    </>
  );
  // render ที่ document.body เสมอ — Drawer ซ้อน Drawer แล้วโดนกล่องแม่ (overflow/transform) ตัดขอบ
  return (window.ReactDOM && window.ReactDOM.createPortal) ? window.ReactDOM.createPortal(node, document.body) : node;
};

// Workflow indicator
const Workflow = ({ steps, current = 0 }) => (
  <div className="workflow">
    {steps.map((s, i) => (
      <React.Fragment key={i}>
        <div className={`step ${i < current ? "done" : i === current ? "current" : ""}`}>
          {i < current && <I.check size={12}/>}
          <span>{s}</span>
        </div>
        {i < steps.length - 1 && <span className="arrow">→</span>}
      </React.Fragment>
    ))}
  </div>
);

// Bar chart — vibrant stacked
const BarChart = ({ data, height = 160, valueKey = "value" }) => {
  const max = Math.max(1, ...data.map(d => Number(d[valueKey]) || ((Number(d.a) || 0) + (Number(d.b) || 0)) || 0));
  return (
    <div className="bar-chart" style={{ height }}>
      {data.map((d, i) => (
        <div className="bar-col" key={i}>
          <div className="bar-stack">
            {d.b !== undefined && <div className="seg alt" style={{ height: `${(d.b / max) * 100}%` }}/>}
            <div className="seg" style={{ height: `${((d.a !== undefined ? d.a : d[valueKey]) / max) * 100}%` }}/>
          </div>
          <div className="bar-label">{d.label || d.m}</div>
        </div>
      ))}
    </div>
  );
};

// Smooth Catmull-Rom → cubic bezier path through points
function smoothPath(pts) {
  if (pts.length < 2) return "";
  let d = `M ${pts[0][0]},${pts[0][1]}`;
  for (let i = 0; i < pts.length - 1; i++) {
    const p0 = pts[i - 1] || pts[i];
    const p1 = pts[i];
    const p2 = pts[i + 1];
    const p3 = pts[i + 2] || p2;
    const t = 0.18;
    const c1x = p1[0] + (p2[0] - p0[0]) * t;
    const c1y = p1[1] + (p2[1] - p0[1]) * t;
    const c2x = p2[0] - (p3[0] - p1[0]) * t;
    const c2y = p2[1] - (p3[1] - p1[1]) * t;
    d += ` C ${c1x},${c1y} ${c2x},${c2y} ${p2[0]},${p2[1]}`;
  }
  return d;
}

// Line chart — smooth curves + gradient area fill
const LineChart = ({ data, series, height = 180 }) => {
  const w = 720;
  const h = height;
  const padL = 46, padR = 16, padT = 16, padB = 28;
  const innerW = w - padL - padR;
  const innerH = h - padT - padB;
  const allVals = series.flatMap(s => (data || []).map(d => Number(d[s.key]))).filter(v => isFinite(v));
  const hasData = allVals.some(v => v > 0);
  const max = (allVals.length ? Math.max(...allVals) : 0) * 1.15 || 1;
  const min = 0;
  const x = (i) => padL + (data.length > 1 ? (i / (data.length - 1)) : 0.5) * innerW;
  const y = (v) => padT + innerH - (((Number(v) || 0) - min) / (max - min)) * innerH;
  const fmtK = (v) => { v = Math.round(v); const a = Math.abs(v); if (a >= 1e6) return (v / 1e6).toFixed(a >= 1e7 ? 0 : 1).replace(/\.0$/, "") + "M"; if (a >= 1000) return Math.round(v / 1000) + "k"; return String(v); };
  const uid = React.useMemo(() => "lc" + Math.random().toString(36).slice(2, 7), []);
  return (
    <svg viewBox={`0 0 ${w} ${h}`} style={{ width: "100%", height: "auto", display: "block" }}>
      <defs>
        {series.map((s, idx) => (
          <linearGradient key={s.key} id={`${uid}-g${idx}`} x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor={s.color} stopOpacity={s.dashed ? 0.12 : 0.22} />
            <stop offset="100%" stopColor={s.color} stopOpacity="0" />
          </linearGradient>
        ))}
      </defs>
      {/* grid + Y labels */}
      {[0, 0.25, 0.5, 0.75, 1].map((t, i) => (
        <g key={i}>
          <line x1={padL} y1={padT + innerH * t} x2={w - padR} y2={padT + innerH * t} stroke="var(--line)" strokeWidth="1" strokeDasharray="2 5" vectorEffect="non-scaling-stroke" />
          {hasData && <text x={padL - 8} y={padT + innerH * t + 4} fontSize="10" textAnchor="end" fill="var(--ink-4)" fontFamily="var(--font-mono)">{fmtK(max - (max - min) * t)}</text>}
        </g>
      ))}
      {hasData && series.map((s, idx) => {
        const pts = data.map((d, i) => [x(i), y(d[s.key])]);
        const line = smoothPath(pts);
        const area = `${line} L ${x(data.length - 1)},${padT + innerH} L ${x(0)},${padT + innerH} Z`;
        return (
          <g key={s.key}>
            <path d={area} fill={`url(#${uid}-g${idx})`} />
            <path d={line} fill="none" stroke={s.color} strokeWidth={s.dashed ? 2 : 2.5} strokeDasharray={s.dashed ? "1 6" : ""} strokeLinecap="round" strokeLinejoin="round" vectorEffect="non-scaling-stroke" />
          </g>
        );
      })}
      {hasData && series.map(s => data.map((d, i) => (
        (Number(d[s.key]) > 0) ? <circle key={s.key + i} cx={x(i)} cy={y(d[s.key])} r={s.dashed ? 2.5 : 3} fill="var(--surface)" stroke={s.color} strokeWidth="2" /> : null
      )))}
      {!hasData && (
        <text x={w / 2} y={padT + innerH / 2} fontSize="13" textAnchor="middle" fill="var(--ink-4)">ยังไม่มีข้อมูลในช่วงนี้</text>
      )}
      {/* X labels */}
      {data.map((d, i) => (
        <text key={i} x={x(i)} y={h - 8} fontSize="10.5" textAnchor="middle" fill="var(--ink-3)" fontWeight="500">{d.m || d.label}</text>
      ))}
    </svg>
  );
};

// Sparkline
const Sparkline = ({ data, color = "var(--ink-1)", height = 36, width = 100 }) => {
  const max = Math.max(...data);
  const min = Math.min(...data);
  const range = max - min || 1;
  const step = width / (data.length - 1);
  const pts = data.map((v, i) => `${i * step},${height - ((v - min) / range) * (height - 4) - 2}`).join(" ");
  return (
    <svg width={width} height={height} style={{display: "block"}}>
      <polyline points={pts} fill="none" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
};

// Barcode visual (random bars)
const BarcodeVisual = ({ code, height = 50, width = 200 }) => {
  // deterministic from code
  const pattern = useMemo(() => {
    if (!code) return [];
    let bars = [];
    let seed = 0;
    for (let i = 0; i < code.length; i++) seed += code.charCodeAt(i);
    let r = seed;
    for (let i = 0; i < 50; i++) {
      r = (r * 9301 + 49297) % 233280;
      bars.push(r % 4 + 1);
    }
    return bars;
  }, [code]);
  return (
    <div>
      <div className="barcode" style={{height, width}}>
        {pattern.map((w, i) => (
          <div key={i} className={i % 2 === 0 ? "b" : ""} style={{width: w + "px"}}/>
        ))}
      </div>
      <div className="mono" style={{fontSize: 11, textAlign: "center", marginTop: 4, color: "var(--ink-3)"}}>{code}</div>
    </div>
  );
};

// Switch
const Switch = ({ on, onChange }) => (
  <div className={`switch ${on ? "on" : ""}`} onClick={() => onChange && onChange(!on)}/>
);

// Pill group
const PillGroup = ({ items, value, onChange }) => (
  <div className="pill-group">
    {items.map(it => (
      <div key={it} className={`pill ${value === it ? "on" : ""}`} onClick={() => onChange(it)}>{it}</div>
    ))}
  </div>
);

// Toast manager
let toastId = 0;
const ToastHub = () => {
  const [list, setList] = useState([]);
  useEffect(() => {
    window.toast = (msg) => {
      const id = ++toastId;
      setList(l => [...l, { id, msg }]);
      setTimeout(() => setList(l => l.filter(t => t.id !== id)), 2500);
    };
  }, []);
  return (
    <div className="toast-wrap">
      {list.map(t => <div key={t.id} className="toast"><I.check size={14}/>{t.msg}</div>)}
    </div>
  );
};

// ─── ช่องกรอกชื่อสินค้าแบบค้นหา/เติมอัตโนมัติ (ใช้ซ้ำทุกเอกสาร) ───
function ProductAutocomplete({ value, onChange, onPick, placeholder, className, inputStyle, style }) {
  const D = window.SSSData || {};
  const [open, setOpen] = useState(false);
  const [hi, setHi] = useState(0);
  const boxRef = useRef(null);
  const q = String(value || "").trim().toLowerCase();
  const matches = q
    ? (D.Products || []).filter(p => (((p.name || "") + " " + (p.sku || "") + " " + (p.barcode || "") + " " + (p.category || "")).toLowerCase().includes(q))).slice(0, 8)
    : [];
  useEffect(() => {
    const h = (e) => { if (boxRef.current && !boxRef.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", h);
    return () => document.removeEventListener("mousedown", h);
  }, []);
  const choose = (p) => { onPick && onPick(p); setOpen(false); };
  const onKey = (e) => {
    if (!open || matches.length === 0) return;
    if (e.key === "ArrowDown") { e.preventDefault(); setHi(h => Math.min(h + 1, matches.length - 1)); }
    else if (e.key === "ArrowUp") { e.preventDefault(); setHi(h => Math.max(h - 1, 0)); }
    else if (e.key === "Enter") { e.preventDefault(); choose(matches[Math.min(hi, matches.length - 1)]); }
    else if (e.key === "Escape") setOpen(false);
  };
  return (
    <div ref={boxRef} style={{ position: "relative", ...(style || {}) }}>
      <input className={className || "input"} value={value || ""} placeholder={placeholder} style={inputStyle} autoComplete="off"
        onChange={e => { onChange && onChange(e.target.value); setOpen(true); setHi(0); }}
        onFocus={() => { if (q && matches.length) setOpen(true); }}
        onKeyDown={onKey}/>
      {open && matches.length > 0 && (
        <div style={{ position: "absolute", top: "calc(100% + 4px)", left: 0, right: 0, zIndex: 70, background: "var(--surface)", border: "1px solid var(--line-strong)", borderRadius: 10, boxShadow: "var(--shadow-lg)", overflow: "hidden", maxHeight: 280, overflowY: "auto" }}>
          {matches.map((p, i) => (
            <div key={p.sku || p.id || i} onMouseDown={(e) => { e.preventDefault(); choose(p); }} onMouseEnter={() => setHi(i)}
              style={{ padding: "8px 11px", cursor: "pointer", background: i === hi ? "var(--surface-2)" : "transparent", borderBottom: "1px solid var(--line-soft)" }}>
              <div style={{ fontSize: 13, fontWeight: 500, lineHeight: 1.3 }}>{p.name}</div>
              <div style={{ fontSize: 11, color: "var(--ink-3)", display: "flex", gap: 10, marginTop: 1 }}>
                {p.sku && <span className="mono">{p.sku}</span>}
                {p.price != null && <span>฿{fmt0(p.price)}{p.unit ? " / " + p.unit : ""}</span>}
                {p.stock != null && <span>คงเหลือ {p.stock}</span>}
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ───────── เลือกเวลาแบบไทย 24 ชม. (ชั่วโมง:นาที) — ไม่พึ่ง locale ของเบราว์เซอร์ (Safari ไม่บังคับ 24 ชม.) ─────────
function TimeField({ value, onChange, minuteStep = 1, className, style, disabled }) {
  const v = /^\d{1,2}:\d{2}$/.test(value || "") ? value : "";
  const hh = v ? v.split(":")[0].padStart(2, "0") : "";
  const mm = v ? v.split(":")[1] : "";
  const set = (h, m) => onChange && onChange((h || "00") + ":" + (m || "00"));
  const hours = Array.from({ length: 24 }, (_, i) => String(i).padStart(2, "0"));
  const mins = Array.from({ length: Math.ceil(60 / minuteStep) }, (_, i) => String(i * minuteStep).padStart(2, "0"));
  const sel = { padding: "0 6px", height: 36, border: "1px solid var(--line)", borderRadius: 8, background: "var(--surface)", color: "var(--ink-1)", fontSize: 13.5, fontFamily: "inherit" };
  return (
    <div style={{ display: "inline-flex", alignItems: "center", gap: 4, ...style }} className={className}>
      <select disabled={disabled} value={hh} onChange={e => set(e.target.value, mm || "00")} style={sel} aria-label="ชั่วโมง">
        <option value="">--</option>
        {hours.map(h => <option key={h} value={h}>{h}</option>)}
      </select>
      <b style={{ color: "var(--ink-3)" }}>:</b>
      <select disabled={disabled} value={mm} onChange={e => set(hh || "00", e.target.value)} style={sel} aria-label="นาที">
        <option value="">--</option>
        {mins.map(m => <option key={m} value={m}>{m}</option>)}
      </select>
      <span style={{ fontSize: 11, color: "var(--ink-3)", marginLeft: 2 }}>น.</span>
    </div>
  );
}

window.UI = { Btn, Badge, Stat, Card, PageHead, Tabs, Drawer, Workflow, BarChart, LineChart, Sparkline, BarcodeVisual, Switch, PillGroup, ToastHub, ProductAutocomplete, TimeField, fmt, fmtM, fmt0, bahtText };
window.ProductAutocomplete = ProductAutocomplete;

// Generic CSV export — used by list "ส่งออก Excel" buttons
window.exportRowsCSV = function(filename, columns, rows) {
  if (!rows || !rows.length) { window.toast && window.toast("ไม่มีข้อมูลให้ส่งออก"); return; }
  const cols = columns.filter(c => c.key && c.label);
  const esc = (v) => {
    const s = (v === null || v === undefined) ? "" : String(v);
    return /[",\n]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s;
  };
  const lines = [cols.map(c => esc(c.label)).join(",")];
  rows.forEach(r => lines.push(cols.map(c => esc(r[c.key])).join(",")));
  const blob = new Blob(["\uFEFF" + lines.join("\n")], { type: "text/csv;charset=utf-8" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url; a.download = (filename || "export") + "-" + new Date().toISOString().slice(0, 10) + ".csv";
  a.click();
  URL.revokeObjectURL(url);
  window.toast && window.toast("ส่งออก " + rows.length + " รายการแล้ว");
};
