/* ui.jsx — ของใช้ร่วมทุกหน้า: รูปแบบตัวเลข, ไอคอน, ช่องกรอกตัวเลข, modal, toast
 * ไม่มี import/export — ทุกชื่อเป็น global ใช้ร่วมกับไฟล์ .jsx อื่น (ดูลำดับโหลดใน index.html) */
const { useState, useEffect, useMemo, useRef, useCallback } = React;
const R = window.Rules;

/* ── รูปแบบตัวเลข/วันที่ ── */
const fmt = (x) => (x == null || !isFinite(x) ? '–' : Number(x).toLocaleString('en-US', { maximumFractionDigits: 2 }));
const fmtPct = (p) => (p == null || !isFinite(p) ? '' : (p > 0 ? '+' : '') + p.toFixed(1) + '%');
const fmtDateTime = (s) => (s ? new Date(s).toLocaleString('th-TH', { dateStyle: 'medium', timeStyle: 'short' }) : '');
const fmtTime = (s) => (s ? new Date(s).toLocaleTimeString('th-TH', { hour: '2-digit', minute: '2-digit' }) : '');

/** แปลงข้อความเป็นจำนวนเงิน: '' → null (ยังไม่กรอก), ผิดรูปแบบ/ติดลบ → NaN */
function parseNum(text) {
  const t = String(text).replace(/[,\s]/g, '');
  if (t === '') return null;
  const v = Number(t);
  return isFinite(v) && v >= 0 && v <= 1e11 ? Math.round(v * 100) / 100 : NaN;
}

const STATUS = {
  draft: { label: 'กำลังกรอก', cls: '' },
  submitted: { label: 'ส่งแล้ว รอตรวจ', cls: 'blue' },
  returned: { label: 'ตีกลับให้แก้', cls: 'yellow' },
  locked: { label: 'ล็อกแล้ว', cls: 'green' },
};
const ROLE_LABEL = { admin: 'ผู้ดูแล', central: 'บัญชี (ส่วนกลาง)', dept: 'ฝ่าย' };

/* ── ไอคอน (เส้น 24×24) ── */
const ICONS = {
  chevron: 'M6 9l6 6 6-6',
  check: 'M5 12l5 5L20 7',
  alert: 'M12 9v4m0 4h.01M10.3 3.9L2.4 18a2 2 0 001.7 3h15.8a2 2 0 001.7-3L13.7 3.9a2 2 0 00-3.4 0z',
  search: 'M11 19a8 8 0 100-16 8 8 0 000 16zm10 2l-4.3-4.3',
  plus: 'M12 5v14M5 12h14',
  x: 'M6 6l12 12M18 6L6 18',
  download: 'M12 3v12m0 0l-4-4m4 4l4-4M4 21h16',
  lock: 'M6 11V8a6 6 0 0112 0v3M5 11h14v10H5z',
  undo: 'M9 14L4 9l5-5M4 9h10a6 6 0 010 12h-3',
  copy: 'M9 9h11v11H9zM5 15H4V4h11v1',
  back: 'M15 18l-6-6 6-6',
  key: 'M15 7a4 4 0 11-3.9 5H8v3H5v3H2v-3l7.1-7.1A4 4 0 0115 7z',
  logout: 'M9 21H5V3h4m7 14l5-5-5-5m5 5H9',
  note: 'M5 4h14v12l-4 4H5zM9 9h6M9 13h4',
  eye: 'M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12zm10 3a3 3 0 100-6 3 3 0 000 6z',
  eyeOff: 'M3 3l18 18M10.6 5.1A10.7 10.7 0 0112 5c6.5 0 10 7 10 7a17.7 17.7 0 01-3.2 4.2M6.3 6.3C3.6 8.1 2 12 2 12s3.5 7 10 7c1.7 0 3.3-.4 4.7-1.1M9.9 9.9a3 3 0 004.2 4.2',
};
function Icon({ name, className }) {
  return (
    <svg className={'icon ' + (className || '')} viewBox="0 0 24 24" aria-hidden="true">
      <path d={ICONS[name] || ''} />
    </svg>
  );
}

/**
 * ช่องกรอกจำนวนเงิน — ตอนไม่ได้โฟกัสโชว์ตัวเลขมีจุลภาค, ตอนพิมพ์โชว์ตามที่พิมพ์
 * nav: ชื่อกลุ่มสำหรับเลื่อนด้วย Enter/ลูกศรขึ้นลง ไปช่องถัดไปในกลุ่มเดียวกัน
 */
function NumInput({ value, onValue, disabled, className, placeholder, ariaLabel, nav }) {
  const [text, setText] = useState(null);
  const shown = text !== null ? text : value == null ? '' : fmt(value);
  const invalid = text !== null && Number.isNaN(parseNum(text));
  const move = (el, step) => {
    const all = Array.from(document.querySelectorAll('input[data-nav="' + nav + '"]:not(:disabled)'));
    const next = all[all.indexOf(el) + step];
    if (next) next.focus(); else el.blur();
  };
  return (
    <input
      type="text" inputMode="decimal" autoComplete="off"
      className={(className || '') + (invalid ? ' invalid' : '')}
      value={shown} disabled={disabled} placeholder={placeholder} aria-label={ariaLabel} data-nav={nav}
      onFocus={(e) => { const el = e.target; setText(value == null ? '' : String(value)); requestAnimationFrame(() => el.select()); }}
      onChange={(e) => {
        const t = e.target.value;
        setText(t);
        const v = parseNum(t);
        if (!Number.isNaN(v)) onValue(v);
      }}
      onBlur={() => setText(null)}
      onKeyDown={(e) => {
        if (!nav) { if (e.key === 'Enter') e.target.blur(); return; }
        if (e.key === 'Enter' || e.key === 'ArrowDown') { e.preventDefault(); move(e.target, 1); }
        else if (e.key === 'ArrowUp') { e.preventDefault(); move(e.target, -1); }
      }}
    />
  );
}

function Modal({ title, onClose, children, footer, wide }) {
  useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [onClose]);
  return (
    <div className="overlay" onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className={'modal' + (wide ? ' wide' : '')} role="dialog" aria-modal="true" aria-label={title}>
        <div className="modal-h">
          <h2>{title}</h2>
          <button className="btn ghost icon-only" onClick={onClose} aria-label="ปิด"><Icon name="x" /></button>
        </div>
        <div className="modal-b">{children}</div>
        {footer && <div className="modal-f">{footer}</div>}
      </div>
    </div>
  );
}

/* ── toast: เรียก toast('ข้อความ') หรือ toast('ข้อความ', 'error') จากที่ไหนก็ได้ ── */
function toast(message, kind) {
  window.dispatchEvent(new CustomEvent('wpb-toast', { detail: { message, kind: kind || 'info' } }));
}
function ToastHost() {
  const [items, setItems] = useState([]);
  useEffect(() => {
    const onToast = (e) => {
      const id = Date.now() + Math.random();
      setItems((xs) => [...xs, { id, ...e.detail }]);
      setTimeout(() => setItems((xs) => xs.filter((x) => x.id !== id)), e.detail.kind === 'error' ? 6000 : 3500);
    };
    window.addEventListener('wpb-toast', onToast);
    return () => window.removeEventListener('wpb-toast', onToast);
  }, []);
  return (
    <div className="toasts" role="status" aria-live="polite">
      {items.map((t) => <div key={t.id} className={'toast ' + t.kind}>{t.message}</div>)}
    </div>
  );
}

/** ช่องรหัสผ่านที่กดรูปตาเพื่อดู/ซ่อนสิ่งที่พิมพ์ได้ (ใช้ภายใน <label className="field">) */
function PasswordInput({ value, onChange, autoComplete, autoFocus }) {
  const [show, setShow] = useState(false);
  return (
    <span className="pw">
      <input type={show ? 'text' : 'password'} value={value} onChange={onChange} autoComplete={autoComplete} autoFocus={autoFocus} />
      <button
        type="button" className="pw-toggle" onClick={() => setShow(!show)}
        aria-label={show ? 'ซ่อนรหัสผ่าน' : 'แสดงรหัสผ่าน'} title={show ? 'ซ่อนรหัสผ่าน' : 'แสดงรหัสผ่าน'}
      >
        <Icon name={show ? 'eyeOff' : 'eye'} />
      </button>
    </span>
  );
}

function StatusBadge({ status }) {
  const s = STATUS[status] || STATUS.draft;
  return <span className={'badge ' + s.cls}>{s.label}</span>;
}

/** ช่วงเดือนที่มียอดใช้จริง เช่น "ม.ค.–ส.ค." */
function actualRange(round) {
  const n = round ? round.actualMonths : 0;
  if (!n) return 'ยังไม่มี';
  return n === 1 ? R.MONTHS_TH[0] : R.MONTHS_TH[0] + '–' + R.MONTHS_TH[n - 1];
}

/** สรุปยอดของชุดบรรทัด (ใช้ทั้งหน้ากรอกและหน้าผู้ดูแล) */
function summarize(lines, cfg) {
  const s = { n: 0, filled: 0, own: 0, ownFilled: 0, central: 0, centralFilled: 0, B: 0, A: 0, P: 0, R: 0, red: 0, yellow: 0, noteMissing: 0 };
  for (const l of lines) {
    const ev = R.evaluateLine(l, cfg);
    s.n++; s.B += ev.B; s.A += ev.A; s.P += ev.P || 0; s.R += ev.R;
    if (ev.filled) s.filled++;
    if (l.filler === 'central') { s.central++; if (ev.filled) s.centralFilled++; }
    else { s.own++; if (ev.filled) s.ownFilled++; }
    if (ev.flag === 'red') s.red++;
    if (ev.flag === 'yellow') s.yellow++;
    if (ev.noteMissing) s.noteMissing++;
  }
  s.pct = s.B > 0 ? ((s.R - s.B) / s.B) * 100 : null;
  return s;
}
