/* TaxFlow — shared primitives, sidebar, topbar, charts, modal */
/* globals React, ReactDOM */

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

/* --------------------------- ICONS (inline SVG) --------------------------- */

const I = {
  grid: (p) => (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" {...p}>
      <rect x="3.5" y="3.5" width="7" height="7" rx="1.5"/><rect x="13.5" y="3.5" width="7" height="7" rx="1.5"/>
      <rect x="3.5" y="13.5" width="7" height="7" rx="1.5"/><rect x="13.5" y="13.5" width="7" height="7" rx="1.5"/>
    </svg>
  ),
  trend: (p) => (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" {...p}>
      <path d="M3 17L9 11L13 15L21 7"/><path d="M21 12V7H16"/>
    </svg>
  ),
  doc: (p) => (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" {...p}>
      <path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z"/><path d="M14 3v5h5"/>
      <path d="M9 13h6M9 17h4"/>
    </svg>
  ),
  bag: (p) => (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" {...p}>
      <path d="M5 8h14l-1 12a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 8z"/>
      <path d="M9 8V6a3 3 0 0 1 6 0v2"/>
    </svg>
  ),
  cart: (p) => (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" {...p}>
      <circle cx="9" cy="20" r="1.4"/><circle cx="17" cy="20" r="1.4"/>
      <path d="M3 4h2l2.4 12.2a2 2 0 0 0 2 1.6h7.6a2 2 0 0 0 2-1.6L21 8H6"/>
    </svg>
  ),
  users: (p) => (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" {...p}>
      <rect x="4" y="4" width="16" height="16" rx="2"/><path d="M8 9h8M8 13h8M8 17h5"/>
    </svg>
  ),
  audit: (p) => (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" {...p}>
      <circle cx="9" cy="8" r="3"/><path d="M3 20c1.2-3 3.4-4.5 6-4.5s4.8 1.5 6 4.5"/>
      <circle cx="17" cy="9" r="2"/><path d="M14.5 14c.5-.7 1.4-1.2 2.5-1.2 1.6 0 3 1 3.5 2.5"/>
    </svg>
  ),
  trash: (p) => (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" {...p}>
      <path d="M4 7h16"/><path d="M9 7V5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2"/>
      <path d="M6 7l1 13a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2l1-13"/><path d="M10 11v7M14 11v7"/>
    </svg>
  ),
  plus: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" {...p}><path d="M12 5v14M5 12h14"/></svg>),
  search: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" {...p}><circle cx="11" cy="11" r="7"/><path d="M20 20l-3.5-3.5"/></svg>),
  download: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M12 4v12"/><path d="M7 11l5 5 5-5"/><path d="M5 20h14"/></svg>),
  chevDown: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M6 9l6 6 6-6"/></svg>),
  arrowUp: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M7 17L17 7"/><path d="M9 7h8v8"/></svg>),
  arrowDown: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M7 7l10 10"/><path d="M17 9v8H9"/></svg>),
  edit: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M14 4l6 6L8 22H2v-6z"/><path d="M13 5l6 6"/></svg>),
  x: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" {...p}><path d="M6 6l12 12M18 6L6 18"/></svg>),
  check: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M5 12l5 5L20 7"/></svg>),
  clip: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M21 11.5l-8.5 8.5a5 5 0 0 1-7-7L14 4.5a3.5 3.5 0 1 1 5 5L10.5 18a2 2 0 1 1-3-3l7-7"/></svg>),
  restore: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M3 12a9 9 0 1 0 3-6.7"/><path d="M3 4v5h5"/></svg>),
  bell: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M6 8a6 6 0 0 1 12 0c0 7 3 8 3 8H3s3-1 3-8"/><path d="M10 21a2 2 0 0 0 4 0"/></svg>),
  sun: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" {...p}><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4 12H2M22 12h-2M5 5l1.5 1.5M17.5 17.5L19 19M5 19l1.5-1.5M17.5 6.5L19 5"/></svg>),
  moon: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M21 13A9 9 0 1 1 11 3a7 7 0 0 0 10 10z"/></svg>),
  filter: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M4 5h16l-6 8v6l-4-2v-4z"/></svg>),
  sparkle: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M12 3l1.8 4.7L18 9.5l-4.2 1.8L12 16l-1.8-4.7L6 9.5l4.2-1.8z"/><path d="M19 16l.8 1.8L21 19l-1.2.2L19 21l-.8-1.8L17 19l1.2-1z"/></svg>),
};
window.I = I;

/* --------------------------- TINY UTILS --------------------------- */

function cls(...a){ return a.filter(Boolean).join(" "); }
window.cls = cls;

/* Animated number counter — smooth easing */
function NumberCounter({ value, decimals = 2, duration = 900, prefix = "฿", suffix = "" }) {
  const [v, setV] = useState(value);
  const fromRef = useRef(value);
  useEffect(() => {
    const from = fromRef.current;
    const to = value;
    const start = performance.now();
    let raf;
    const tick = (t) => {
      const k = Math.min(1, (t - start) / duration);
      const e = 1 - Math.pow(1 - k, 3); // easeOutCubic
      const cur = from + (to - from) * e;
      setV(cur);
      if (k < 1) raf = requestAnimationFrame(tick);
      else fromRef.current = to;
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [value, duration]);
  const s = v.toLocaleString("en-US", { minimumFractionDigits: decimals, maximumFractionDigits: decimals });
  return (
    <span className="num">
      {prefix && <span className="num-pfx">{prefix}</span>}
      <span className="num-v tabnum">{s}</span>
      {suffix && <span className="num-sfx">{suffix}</span>}
    </span>
  );
}
window.NumberCounter = NumberCounter;

/* --------------------------- SIDEBAR --------------------------- */

const NAV = [
  { id: "dashboard", label: "Dashboard",         icon: I.grid,  section: "ภาพรวม" },
  { id: "pl",        label: "กำไร/ขาดทุน",        icon: I.trend, section: "ภาพรวม" },
  { id: "vat",       label: "VAT Report",        icon: I.doc,   section: "ภาพรวม" },
  { id: "sales",     label: "ขาย",                icon: I.bag,   section: "รายการ" },
  { id: "purchases", label: "ซื้อ",                icon: I.cart,  section: "รายการ" },
  { id: "suppliers", label: "ผู้ขาย",              icon: I.users, section: "รายการ" },
  { id: "audit",     label: "Audit Log",         icon: I.audit, section: "กิจกรรม" },
  { id: "trash",     label: "ถังขยะ",              icon: I.trash, section: "กิจกรรม" },
];
window.NAV = NAV;

function Sidebar({ page, setPage, dark, setDark }) {
  // Group nav by section, preserve order
  const groups = [];
  let curSec = null, curGroup = null;
  for (const n of NAV) {
    if (n.section !== curSec) { curSec = n.section; curGroup = { section: curSec, items: [] }; groups.push(curGroup); }
    curGroup.items.push(n);
  }
  return (
    <aside className="side">
      <div className="brand">
        <div className="brand-mark">
          <svg viewBox="0 0 32 32" width="22" height="22" fill="none">
            <rect x="2" y="2" width="28" height="28" rx="9" fill="var(--accent)"/>
            <path d="M9 11h14M16 11v13" stroke="white" strokeWidth="2.4" strokeLinecap="round"/>
            <circle cx="22.5" cy="20.5" r="3.5" fill="white"/>
            <path d="M21 20.5l1.2 1.2L24.5 19.5" stroke="var(--accent)" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
        </div>
        <div>
          <div className="brand-name">TaxFlow</div>
          <div className="brand-sub">VAT &amp; Bookkeeping</div>
        </div>
      </div>

      <nav className="nav">
        {groups.map((g) => (
          <div className="nav-group" key={g.section}>
            <div className="nav-section">{g.section}</div>
            {g.items.map((n) => {
              const Icon = n.icon;
              const active = page === n.id;
              return (
                <button key={n.id} className={cls("nav-item", active && "is-active")} onClick={() => setPage(n.id)}>
                  <span className="nav-ic"><Icon width="18" height="18"/></span>
                  <span className="nav-lb">{n.label}</span>
                  {active && <span className="nav-pill" aria-hidden/>}
                </button>
              );
            })}
          </div>
        ))}
      </nav>

      <div className="side-foot">
        <button className="theme-toggle" onClick={() => setDark(!dark)} aria-label="Toggle theme">
          <span className={cls("tt-knob", dark && "is-dark")}>
            {dark ? <I.moon width="14" height="14"/> : <I.sun width="14" height="14"/>}
          </span>
          <span className="tt-lb">{dark ? "Dark" : "Light"}</span>
        </button>
        
      </div>
    </aside>
  );
}
window.Sidebar = Sidebar;

/* --------------------------- TOPBAR --------------------------- */

function Topbar({ title, subtitle, right, search, onSearch, searchPlaceholder }) {
  return (
    <header className="topbar">
      <div className="topbar-l">
        <div className="topbar-title">{title}</div>
        {subtitle && <div className="topbar-sub">{subtitle}</div>}
      </div>
      {search !== undefined && (
        <div className="topbar-search">
          <I.search width="16" height="16"/>
          <input
            value={search}
            onChange={(e) => onSearch(e.target.value)}
            placeholder={searchPlaceholder || "ค้นหา…"}
          />
          {search && <button className="x-btn" onClick={() => onSearch("")}><I.x width="14" height="14"/></button>}
        </div>
      )}
      <div className="topbar-r">{right}</div>
    </header>
  );
}
window.Topbar = Topbar;

/* --------------------------- MONTH PICKER --------------------------- */

function MonthPicker({ value, onChange }) {
  // value: { y, m } 1-based month
  const [open, setOpen] = useState(false);
  const ref = useRef();
  useEffect(() => {
    const fn = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", fn);
    return () => document.removeEventListener("mousedown", fn);
  }, []);
  const label = `${window.THAI_MONTH[value.m - 1]} ${value.y + 543}`;
  return (
    <div className="mp" ref={ref}>
      <button className="mp-btn" onClick={() => setOpen(!open)}>
        <span>{label}</span>
        <I.chevDown width="14" height="14"/>
      </button>
      {open && (
        <div className="mp-pop">
          <div className="mp-yr">
            <button onClick={() => onChange({ ...value, y: value.y - 1 })}>‹</button>
            <span>{value.y + 543}</span>
            <button onClick={() => onChange({ ...value, y: value.y + 1 })}>›</button>
          </div>
          <div className="mp-grid">
            {window.THAI_MONTH_SHORT.map((m, i) => (
              <button
                key={m}
                className={cls("mp-cell", value.m === i + 1 && "is-on")}
                onClick={() => { onChange({ ...value, m: i + 1 }); setOpen(false); }}
              >{m}</button>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}
window.MonthPicker = MonthPicker;

/* --------------------------- PERIOD BAR — chips + month picker --------------------------- */

function PeriodBar({ value, onChange, anchor }) {
  // anchor = today's "now" month for chip semantics; defaults to seed (May 2026)
  const now = anchor || { y: 2026, m: 5 };
  const isSame = (a, b) => a && b && a.y === b.y && a.m === b.m;
  const lastMo = window.prevMonth(now);

  const chip = (lb, m, key) => (
    <button
      key={key}
      className={cls("chip", isSame(value, m) && "is-on")}
      onClick={() => onChange(m)}
    >{lb}</button>
  );

  return (
    <div className="period-bar">
      {chip("เดือนนี้", now, "this")}
      {chip("เดือนก่อน", lastMo, "last")}
      <span className="chip-sep" aria-hidden></span>
      <MonthPicker value={value} onChange={onChange}/>
    </div>
  );
}
window.PeriodBar = PeriodBar;

/* --------------------------- AREA CHART (draw-on) --------------------------- */

function AreaChart({ data, height = 240, ymax }) {
  // data: [{ label, value }]
  const W = 800, H = height, pad = { l: 48, r: 16, t: 24, b: 32 };
  const max = ymax ?? Math.max(1800, ...data.map(d => d.value)) * 1.05;
  const xs = (i) => pad.l + (i * (W - pad.l - pad.r)) / Math.max(1, data.length - 1);
  const ys = (v) => pad.t + (H - pad.t - pad.b) * (1 - v / max);

  // smooth path via catmull-rom -> bezier
  const pts = data.map((d, i) => [xs(i), ys(d.value)]);
  const path = pts.reduce((p, [x, y], i) => {
    if (i === 0) return `M ${x} ${y}`;
    const [px, py] = pts[i - 1];
    const cx1 = px + (x - px) / 2, cy1 = py;
    const cx2 = px + (x - px) / 2, cy2 = y;
    return p + ` C ${cx1} ${cy1} ${cx2} ${cy2} ${x} ${y}`;
  }, "");
  const area = path + ` L ${xs(data.length - 1)} ${H - pad.b} L ${xs(0)} ${H - pad.b} Z`;

  // gridlines (4 horizontal)
  const gridY = [0, 0.33, 0.66, 1].map((k) => pad.t + (H - pad.t - pad.b) * k);

  // animate path draw
  const pathRef = useRef(null);
  const [len, setLen] = useState(0);
  useLayoutEffect(() => {
    if (!pathRef.current) return;
    const l = pathRef.current.getTotalLength();
    setLen(l);
    pathRef.current.style.strokeDasharray = l;
    pathRef.current.style.strokeDashoffset = l;
    requestAnimationFrame(() => {
      pathRef.current.style.transition = "stroke-dashoffset 1100ms cubic-bezier(.2,.7,.2,1)";
      pathRef.current.style.strokeDashoffset = 0;
    });
  }, [data]);

  // hover
  const [hov, setHov] = useState(-1);
  const svgRef = useRef();
  const onMove = (e) => {
    const r = svgRef.current.getBoundingClientRect();
    const x = ((e.clientX - r.left) / r.width) * W;
    let best = 0, bd = Infinity;
    pts.forEach(([px], i) => { const d = Math.abs(px - x); if (d < bd) { bd = d; best = i; } });
    setHov(best);
  };

  return (
    <div className="chart-wrap">
      <svg ref={svgRef} viewBox={`0 0 ${W} ${H}`} className="chart" preserveAspectRatio="none"
           onMouseMove={onMove} onMouseLeave={() => setHov(-1)}>
        <defs>
          <linearGradient id="areaG" x1="0" x2="0" y1="0" y2="1">
            <stop offset="0%" stopColor="var(--accent)" stopOpacity="0.28"/>
            <stop offset="100%" stopColor="var(--accent)" stopOpacity="0"/>
          </linearGradient>
        </defs>

        {/* gridlines */}
        {gridY.map((y, i) => (
          <line key={i} x1={pad.l} x2={W - pad.r} y1={y} y2={y} stroke="var(--line-soft)" strokeWidth="1" strokeDasharray="3 5"/>
        ))}
        {/* y labels */}
        {[1, 0.66, 0.33, 0].map((k, i) => (
          <text key={i} x={pad.l - 8} y={pad.t + (H - pad.t - pad.b) * (1 - k) + 4}
                textAnchor="end" className="ax-l">
            {Math.round(max * k).toLocaleString()}
          </text>
        ))}
        {/* x labels */}
        {data.map((d, i) => (
          <text key={i} x={xs(i)} y={H - 10} textAnchor="middle" className="ax-l">{d.label}</text>
        ))}

        {/* area */}
        <path d={area} fill="url(#areaG)" opacity={len ? 1 : 0} style={{ transition: "opacity 700ms 600ms" }}/>
        {/* line */}
        <path ref={pathRef} d={path} fill="none" stroke="var(--accent)" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"/>

        {/* points */}
        {pts.map(([x, y], i) => (
          <g key={i} className={cls("ch-pt", hov === i && "is-hov")} style={{ transitionDelay: `${600 + i * 80}ms` }}>
            <circle cx={x} cy={y} r="5" fill="var(--bg)" stroke="var(--accent)" strokeWidth="2"/>
          </g>
        ))}

        {/* hover marker */}
        {hov >= 0 && (
          <g>
            <line x1={pts[hov][0]} x2={pts[hov][0]} y1={pad.t} y2={H - pad.b} stroke="var(--line)" strokeWidth="1"/>
            <circle cx={pts[hov][0]} cy={pts[hov][1]} r="6" fill="var(--accent)"/>
          </g>
        )}
      </svg>

      {hov >= 0 && (
        <div className="chart-tip" style={{
          left: `${(pts[hov][0] / W) * 100}%`,
          top:  `${(pts[hov][1] / H) * 100}%`,
        }}>
          <div className="chart-tip-lb">{data[hov].label}</div>
          <div className="chart-tip-v">{window.formatBaht(data[hov].value)}</div>
        </div>
      )}
    </div>
  );
}
window.AreaChart = AreaChart;

/* --------------------------- SPARKLINE --------------------------- */

function Sparkline({ data, height = 36, width = 120 }) {
  const max = Math.max(...data, 1);
  const min = Math.min(...data, 0);
  const range = max - min || 1;
  const pts = data.map((v, i) => [
    (i / (data.length - 1)) * width,
    height - ((v - min) / range) * (height - 4) - 2,
  ]);
  const d = pts.reduce((p, [x, y], i) => p + (i === 0 ? `M ${x} ${y}` : ` L ${x} ${y}`), "");
  return (
    <svg viewBox={`0 0 ${width} ${height}`} width={width} height={height} className="spark">
      <path d={d} fill="none" stroke="var(--accent)" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}
window.Sparkline = Sparkline;

/* --------------------------- MODAL --------------------------- */

function Modal({ open, onClose, title, children, footer, width = 560 }) {
  useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, [open, onClose]);

  if (!open) return null;
  return (
    <div className="modal-bd" onClick={onClose}>
      <div className="modal" style={{ width }} onClick={(e) => e.stopPropagation()}>
        <div className="modal-h">
          <div className="modal-t">{title}</div>
          <button className="x-btn" onClick={onClose}><I.x width="16" height="16"/></button>
        </div>
        <div className="modal-b">{children}</div>
        {footer && <div className="modal-f">{footer}</div>}
      </div>
    </div>
  );
}
window.Modal = Modal;

/* --------------------------- BADGES & BUTTONS --------------------------- */

function Badge({ children, tone = "neutral" }) {
  return <span className={cls("badge", `tone-${tone}`)}>{children}</span>;
}
window.Badge = Badge;

function Btn({ variant = "default", className, children, ...rest }) {
  return <button className={cls("btn", `btn-${variant}`, className)} {...rest}>{children}</button>;
}
window.Btn = Btn;

function IconBtn({ title, children, ...rest }) {
  return <button className="icon-btn" title={title} {...rest}>{children}</button>;
}
window.IconBtn = IconBtn;

/* --------------------------- TOAST --------------------------- */

let toastSetter = null;
function ToastHost() {
  const [items, setItems] = useState([]);
  useEffect(() => {
    toastSetter = (msg, tone = "ok") => {
      const id = Math.random().toString(36).slice(2);
      setItems((s) => [...s, { id, msg, tone }]);
      setTimeout(() => setItems((s) => s.filter((x) => x.id !== id)), 2600);
    };
  }, []);
  return (
    <div className="toast-host">
      {items.map((t) => (
        <div key={t.id} className={cls("toast", `toast-${t.tone}`)}>
          <span className="toast-ic">
            {t.tone === "ok" ? <I.check width="16" height="16"/> : <I.x width="16" height="16"/>}
          </span>
          {t.msg}
        </div>
      ))}
    </div>
  );
}
function toast(msg, tone = "ok") { toastSetter && toastSetter(msg, tone); }
window.ToastHost = ToastHost;
window.toast = toast;
