/* 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"
};
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 = ({ label, value, delta, deltaDir, sub, icon }) => (
  <div className="stat">
    <div className="label">
      {icon ? React.createElement(icon, { size: 14 }) : 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 }) => {
  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;
  return (
    <>
      <div className="scrim" onClick={onClose}/>
      <div className={`drawer ${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>
    </>
  );
};

// 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(...data.map(d => d[valueKey] || (d.a + d.b) || 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 (graffiti vibe)
const LineChart = ({ data, series, height = 180 }) => {
  const w = 600;
  const h = height;
  const padL = 42, padR = 14, padT = 16, padB = 26;
  const innerW = w - padL - padR;
  const innerH = h - padT - padB;
  const allVals = series.flatMap(s => data.map(d => d[s.key]));
  const max = Math.max(...allVals) * 1.12;
  const min = 0;
  const x = (i) => padL + (i / (data.length - 1)) * innerW;
  const y = (v) => padT + innerH - ((v - min) / (max - min)) * innerH;
  const uid = React.useMemo(() => "lc" + Math.random().toString(36).slice(2, 7), []);
  return (
    <svg viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" style={{width: "100%", height}}>
      <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.16 : 0.30}/>
            <stop offset="100%" stopColor={s.color} stopOpacity="0"/>
          </linearGradient>
        ))}
      </defs>
      {/* grid */}
      {[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"/>
          <text x={padL - 8} y={padT + innerH * t + 4} fontSize="10" textAnchor="end" fill="var(--ink-4)" fontFamily="var(--font-mono)">{Math.round((max - (max - min) * t) / 1000)}k</text>
        </g>
      ))}
      {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.5 : 3.5} strokeDasharray={s.dashed ? "2 7" : ""} strokeLinecap="round" strokeLinejoin="round"
              style={{filter: s.dashed ? "none" : "drop-shadow(0 3px 5px rgba(60,30,110,.28))"}}/>
            {data.map((d, i) => (
              <circle key={i} cx={x(i)} cy={y(d[s.key])} r={s.dashed ? 3 : 4} fill="var(--surface)" stroke={s.color} strokeWidth="2.5"/>
            ))}
          </g>
        );
      })}
      {data.map((d, i) => (
        <text key={i} x={x(i)} y={h - 8} fontSize="11" 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>
  );
};

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

// 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 + " รายการแล้ว");
};
