/* INDIEKOS — shared presentational components */
const { useState, useEffect, useRef, useMemo } = React;
const D = window.DATA;

/* ---- Pill mapping ---- */
const INV_STATUS = {
  paid:    { pill: "pill-success", label: "Lunas" },
  partial: { pill: "pill-info",    label: "Sebagian" },
  open:    { pill: "pill-muted",   label: "Open" },
  overdue: { pill: "pill-danger",  label: "Menunggak" },
  pending: { pill: "pill-gold",    label: "Menunggu verifikasi" },
  reserved:{ pill: "pill-gold",    label: "Reserved (DP)" },
  empty:   { pill: "pill-muted",   label: "Kosong" },
};
function Pill({ status, label, lg, dot = true }) {
  const m = INV_STATUS[status] || { pill: "pill-muted", label: label || status };
  return <span className={"pill " + m.pill + (lg ? " pill-lg" : "")}>{dot && <i className="dot" />}{label || m.label}</span>;
}
window.Pill = Pill; window.INV_STATUS = INV_STATUS;

/* ---- Source chip (admin / penghuni / bot) ---- */
function SourceChip({ src }) {
  if (!src || src === "—") return <span className="mut" style={{ fontSize: "var(--fs-xs)" }}>—</span>;
  const map = { penghuni: ["user", "Penghuni"], admin: ["shield", "Admin"], bot: ["whatsapp", "Bot WA"] };
  const [ic, lbl] = map[src] || ["user", src];
  return <span className="row gap-1" style={{ fontSize: "var(--fs-xs)", color: "var(--ink-500)", fontWeight: 600 }}><Icon name={ic} size={13} /> {lbl}</span>;
}
window.SourceChip = SourceChip;

/* ---- Avatar / monogram ---- */
function Avatar({ name, size, img }) {
  const cls = "avatar" + (size === "lg" ? " avatar-lg" : size === "xl" ? " avatar-xl" : "");
  const initials = (name || "?").split(" ").slice(0, 2).map(w => w[0]).join("").toUpperCase();
  return <div className={cls}>{img ? <img src={img} alt="" /> : initials}</div>;
}
window.Avatar = Avatar;

/* ---- KPI stat card ---- */
function StatCard({ label, value, unit, chip, chipIcon, sub, delta, pair, of }) {
  return (
    <div className="stat">
      <div className="stat-top">
        <div className="stat-label">{label}</div>
        {chip && <div className={"stat-chip chip-" + chip}><Icon name={chipIcon} size={18} /></div>}
      </div>
      {pair ? (
        <div className="stat-bignum-pair">
          <span className="stat-num">{value}</span>
          <span className="of">/ {of}</span>
        </div>
      ) : (
        <div className="stat-num">{value}{unit && <span className="unit">{unit}</span>}</div>
      )}
      <div className="stat-sub">
        {delta && (
          <span className={"delta " + (delta.dir)}>
            <Icon name={delta.dir === "up" ? "trendUp" : "trendDown"} size={13} />{delta.val}
          </span>
        )}
        {sub}
      </div>
    </div>
  );
}
window.StatCard = StatCard;

/* ---- Section card wrapper ---- */
function Card({ title, sub, action, children, flush, foot, style, className }) {
  return (
    <div className={"card " + (className || "")} style={style}>
      {(title || action) && (
        <div className="card-head">
          <div>
            <h3>{title}</h3>
            {sub && <div className="sub">{sub}</div>}
          </div>
          {action}
        </div>
      )}
      <div className={"card-body" + (flush ? " flush" : "")}>{children}</div>
      {foot && <div className="card-foot">{foot}</div>}
    </div>
  );
}
window.Card = Card;

/* ---- Progress bar (partial payment) ---- */
function PayProgress({ paid, total, status }) {
  const pct = Math.min(100, Math.round((paid / total) * 100));
  const cls = status === "paid" ? "paid" : status === "overdue" ? "overdue" : "";
  return (
    <div style={{ minWidth: 150 }}>
      <div className="pay-line" style={{ marginBottom: 5, fontSize: "var(--fs-xs)" }}>
        <span className="paid-amt">{D.rp(paid)}</span>
        <span className="total-amt">/ {D.rp(total)}</span>
      </div>
      <div className={"progress " + cls}><i style={{ width: pct + "%" }} /></div>
    </div>
  );
}
window.PayProgress = PayProgress;

/* ---- Tabs ---- */
function Tabs({ tabs, active, onChange }) {
  return (
    <div className="tabs">
      {tabs.map(t => (
        <button key={t.id} className={"tab" + (active === t.id ? " active" : "")} onClick={() => onChange(t.id)}>
          {t.label}{t.count != null && <span className="count">{t.count}</span>}
        </button>
      ))}
    </div>
  );
}
window.Tabs = Tabs;

/* ---- Empty state ---- */
function Empty({ icon, title, text, action }) {
  return (
    <div className="empty">
      <div className="ic"><Icon name={icon || "image"} size={24} /></div>
      <h4>{title}</h4>
      {text && <p>{text}</p>}
      {action}
    </div>
  );
}
window.Empty = Empty;

/* ---- Skeleton rows ---- */
function SkeletonRows({ rows = 5, cols = 4 }) {
  return (
    <div style={{ padding: "var(--card-pad)" }}>
      {Array.from({ length: rows }).map((_, i) => (
        <div key={i} className="row gap-4" style={{ padding: "10px 0", borderBottom: "1px solid var(--ink-100)" }}>
          <div className="skel" style={{ width: 34, height: 34, borderRadius: "50%" }} />
          <div className="grow col gap-2">
            <div className="skel" style={{ height: 11, width: "40%" }} />
            <div className="skel" style={{ height: 9, width: "25%" }} />
          </div>
          {Array.from({ length: cols - 1 }).map((_, j) => <div key={j} className="skel" style={{ height: 11, width: 70 }} />)}
        </div>
      ))}
    </div>
  );
}
window.SkeletonRows = SkeletonRows;

/* ---- Revenue vs Expense mini bar chart (SVG) ---- */
function RevExpChart({ data, height = 170 }) {
  const max = (Math.max(0, ...data.map(d => Math.max(d.rev || 0, d.exp || 0))) * 1.1) || 1;
  const W = 100, gap = W / data.length;
  const bw = gap * 0.28;
  return (
    <div>
      <svg viewBox={`0 0 ${W} 56`} preserveAspectRatio="none" style={{ width: "100%", height, display: "block" }}>
        {[0.25, 0.5, 0.75].map(g => <line key={g} x1="0" x2={W} y1={56 - g * 50} y2={56 - g * 50} stroke="var(--ink-100)" strokeWidth="0.4" />)}
        {data.map((d, i) => {
          const cx = i * gap + gap / 2;
          const rh = ((d.rev || 0) / max) * 50, eh = ((d.exp || 0) / max) * 50;
          return (
            <g key={i}>
              <rect x={cx - bw - 1} y={56 - rh} width={bw} height={rh} rx="0.8" fill="var(--brand-primary)" />
              <rect x={cx + 1} y={56 - eh} width={bw} height={eh} rx="0.8" fill="var(--brand-accent)" />
            </g>
          );
        })}
      </svg>
      <div className="row between" style={{ marginTop: 8, fontSize: "var(--fs-2xs)", color: "var(--ink-500)" }}>
        {data.map((d, i) => <span key={i}>{d.m}</span>)}
      </div>
      <div className="row gap-5" style={{ marginTop: 12, fontSize: "var(--fs-xs)", fontWeight: 600 }}>
        <span className="row gap-2"><i style={{ width: 10, height: 10, borderRadius: 2, background: "var(--brand-primary)" }} /> Revenue</span>
        <span className="row gap-2"><i style={{ width: 10, height: 10, borderRadius: 2, background: "var(--brand-accent)" }} /> Expense</span>
      </div>
    </div>
  );
}
window.RevExpChart = RevExpChart;

/* ---- Donut (occupancy) ---- */
function Donut({ pct, size = 120, label, sub, color }) {
  const r = 42, c = 2 * Math.PI * r, off = c * (1 - pct / 100);
  return (
    <div style={{ position: "relative", width: size, height: size }}>
      <svg viewBox="0 0 100 100" style={{ width: size, height: size, transform: "rotate(-90deg)" }}>
        <circle cx="50" cy="50" r={r} fill="none" stroke="var(--ink-200)" strokeWidth="9" />
        <circle cx="50" cy="50" r={r} fill="none" stroke={color || "var(--brand-primary)"} strokeWidth="9"
          strokeDasharray={c} strokeDashoffset={off} strokeLinecap="round" style={{ transition: "stroke-dashoffset 0.8s var(--ease-out)" }} />
      </svg>
      <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", textAlign: "center" }}>
        <div>
          <div style={{ fontSize: 22, fontWeight: 700, letterSpacing: "-0.02em", lineHeight: 1 }} className="tnum">{label}</div>
          {sub && <div style={{ fontSize: "var(--fs-2xs)", color: "var(--ink-500)", marginTop: 2 }}>{sub}</div>}
        </div>
      </div>
    </div>
  );
}
window.Donut = Donut;

/* ---- Toast ---- */
function useToast() {
  const [toasts, setToasts] = useState([]);
  const push = (msg, kind = "success") => {
    const id = Math.random();
    setToasts(t => [...t, { id, msg, kind }]);
    setTimeout(() => setToasts(t => t.filter(x => x.id !== id)), 2800);
  };
  const node = (
    <div style={{ position: "fixed", bottom: 20, left: "50%", transform: "translateX(-50%)", zIndex: 200, display: "flex", flexDirection: "column", gap: 8, alignItems: "center" }}>
      {toasts.map(t => (
        <div key={t.id} className="row gap-2" style={{ background: "var(--ink-900)", color: "#fff", padding: "10px 16px", borderRadius: "var(--r-3)", boxShadow: "var(--shadow-3)", fontSize: "var(--fs-md)", fontWeight: 600, animation: "toastIn .25s var(--ease-out)" }}>
          <Icon name={t.kind === "success" ? "checkCircle" : t.kind === "danger" ? "xCircle" : "bell"} size={17}
            style={{ color: t.kind === "danger" ? "#FF8A6E" : "#7BE0A8" }} />
          {t.msg}
        </div>
      ))}
    </div>
  );
  return [node, push];
}
window.useToast = useToast;

const _toastStyle = document.createElement("style");
_toastStyle.textContent = "@keyframes toastIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}";
document.head.appendChild(_toastStyle);

/* ---- PWA install prompt (Android/desktop beforeinstallprompt + iOS hint) ---- */
function InstallPrompt({ appName }) {
  const [evt, setEvt] = useState(null);
  const [show, setShow] = useState(false);
  const [ios, setIos] = useState(false);
  useEffect(() => {
    if (sessionStorage.getItem("iruma_install_dismiss")) return;
    const standalone = window.matchMedia("(display-mode: standalone)").matches || window.navigator.standalone;
    if (standalone) return;
    const onBip = (e) => { e.preventDefault(); setEvt(e); setShow(true); };
    window.addEventListener("beforeinstallprompt", onBip);
    const isIos = /iphone|ipad|ipod/i.test(navigator.userAgent);
    if (isIos) { setIos(true); setTimeout(() => setShow(true), 1400); }
    return () => window.removeEventListener("beforeinstallprompt", onBip);
  }, []);
  const dismiss = () => { setShow(false); sessionStorage.setItem("iruma_install_dismiss", "1"); };
  const install = async () => { if (evt) { evt.prompt(); await evt.userChoice; setShow(false); } };
  if (!show) return null;
  return (
    <div style={{ position: "fixed", left: "50%", bottom: 18, transform: "translateX(-50%)", zIndex: 180, width: "min(420px, calc(100vw - 28px))" }}>
      <div className="card row gap-3" style={{ padding: 12, boxShadow: "var(--shadow-3)", alignItems: "center" }}>
        <div className="sb-logo" style={{ width: 40, height: 40, background: "var(--brand-primary)", flex: "none" }}><img src="assets/indiekost.png" style={{ width: "100%", height: "100%", objectFit: "cover" }} /></div>
        <div className="grow" style={{ minWidth: 0 }}>
          <div style={{ fontWeight: 700, fontSize: "var(--fs-sm)" }}>Pasang {appName}</div>
          <div className="mut" style={{ fontSize: "var(--fs-xs)" }}>{ios ? "Ketuk Bagikan ⎙ → \u201CTambah ke Layar Utama\u201D" : "Akses cepat dari layar utama, jalan offline."}</div>
        </div>
        {!ios && <button className="btn btn-primary btn-sm" onClick={install}><Icon name="install" size={15} /> Pasang</button>}
        <button className="icon-btn btn-sm" onClick={dismiss}><Icon name="x" size={16} /></button>
      </div>
    </div>
  );
}
window.InstallPrompt = InstallPrompt;
