// ============================================================
// SamuelMarndi.com — Client Portal
// Where clients see projects, progress, files, messages with Samuel.
// ============================================================
// (hooks declared globally in icons.jsx)

// Local AdminCard so portal doesn't depend on admin-views.jsx
function AdminCard({ title, actionLabel, children, noPad }) {
  return (
    <div className="admin-card">
      {(title || actionLabel) && (
        <div className="admin-card-head">
          <span>{title}</span>
          {actionLabel && <a href="#" style={{ fontSize: 12, color: "var(--admin-copper)" }}>{actionLabel}</a>}
        </div>
      )}
      <div className={noPad ? "" : "admin-card-body"}>{children}</div>
    </div>
  );
}

// Message storage — keyed by project id, persisted in localStorage
const MSGS_KEY = "sm_portal_messages";
const DEFAULT_MESSAGES = {
  "factory-vms": [
    { from: "Samuel", time: "Today · 11:42 AM", text: "Plant B is wired up and 12 of 14 cameras are seeing the NVR. Two cameras at the loading bay had a power issue — replaced the PoE injector this morning, should clear by EOD.\n\nWe're still on track for the May 24 cutover." },
    { from: "You", time: "Today · 10:15 AM", text: "Morning — any update on Plant B? Security team wants to start their training next week if possible." },
    { from: "Samuel", time: "Yesterday · 6:08 PM", text: "Sent the updated VMS user manual to you and your security supervisor. Page 14 has the multi-site search walkthrough you asked about." },
    { from: "Samuel", time: "May 9 · 4:22 PM", text: "VMS is configured. Sharing access credentials separately via secure link. Default retention is 30 days on local NVR + 7 days hot cloud backup." },
    { from: "You", time: "May 9 · 2:01 PM", text: "Got it. Looking forward to seeing it live." },
  ],
  "wa-bot": [
    { from: "Samuel", time: "Today · 9:14 AM", text: "Following up — once you share the help center URL and the FAQs you want the bot to prioritise, I can start prototype this week.\n\nFor WhatsApp template approval lead time, plan ~5 business days." },
    { from: "You", time: "May 10 · 4:20 PM", text: "Got the discovery notes. Will share the doc dump by Friday." },
    { from: "Samuel", time: "May 8 · 3:15 PM", text: "Great call. Sharing the 1-pager scope brief now — covers channels (web + WhatsApp), top 5 intents we agreed on, integration needs, and the 6-week timeline." },
  ],
  "saas-mvp": [
    { from: "Samuel", time: "Today · 10:32 AM", text: "iOS beta is on track for May 20 — TestFlight invites will go to your 5 test users on Monday. Backend is stable, just polishing the empty states." },
    { from: "You", time: "Yesterday · 5:48 PM", text: "Demoed the web app to two design partners — they loved the workflow. One ask: can we add a CSV export from the admin?" },
    { from: "Samuel", time: "Yesterday · 6:02 PM", text: "Yes — scoping it into next week's sprint. Adds ~4 hours to admin work, no impact on the iOS milestone." },
  ],
  "cloud-mig": [
    { from: "Samuel", time: "Today · 11:00 AM", text: "Have you had a chance to review the SoW? Once signed I can lock the Phase 1 pilot date for the first of June." },
    { from: "You", time: "May 12 · 7:18 PM", text: "Reviewing with finance team this week. Expect signed copy by Friday." },
  ],
  "amc-2026": [
    { from: "Samuel", time: "May 12 · 2:14 PM", text: "Quarterly review on the calendar for June 12. SLA tracker is at 100% MTD — one P2 ticket about a printer driver was resolved in 28 minutes." },
    { from: "You", time: "May 10 · 11:02 AM", text: "Thanks Samuel. Send me the prep doc the week of?" },
  ],
};

function loadMessages() {
  try {
    const stored = localStorage.getItem(MSGS_KEY);
    if (stored) return JSON.parse(stored);
  } catch (e) {}
  return DEFAULT_MESSAGES;
}
function saveMessages(messages) {
  try { localStorage.setItem(MSGS_KEY, JSON.stringify(messages)); } catch (e) {}
}

// Map API row → portal UI shape
function apiProjectToUi(row) {
  return {
    id:       row.id,
    name:     row.name,
    service:  row.service || "—",
    status:   row.status === "in_progress" ? "In progress"
            : row.status === "planning"    ? "Discovery"
            : row.status === "review"      ? "On hold"
            : row.status === "completed"   ? "Completed"
            : row.status === "on_hold"     ? "On hold"
            : row.status,
    health:   row.health === "green" ? "on-track"
            : row.health === "amber" ? "needs-input"
            : row.health === "red"   ? "delayed"
            : "on-track",
    progress: row.progress || 0,
    value:    row.value ? "₹" + Number(row.value).toLocaleString("en-IN") : "—",
    started:  (row.start_date || "").slice(0, 10),
    deadline: (row.end_date || "").slice(0, 10),
    owner:    row.owner || "Samuel",
    nextMilestone: ((row.milestones || []).find(m => !m.completed) || {}).title || "—",
    milestones: (row.milestones || []).map(m => ({
      stage: m.title,
      date: (m.due_date || "").slice(0, 10),
      done: !!m.completed,
    })),
    client: "You",
    clientEmail: "",
  };
}
function apiMsgToUi(m) {
  return {
    from: m.sender === "client" ? "You" : (m.sender_name || "Samuel"),
    time: m.created_at ? new Date(m.created_at).toLocaleString("en-IN", { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" }) : "",
    text: m.content,
  };
}

function ClientPortal({ user, onLogout }) {
  const [allProjects, setAllProjects] = useState([]);
  const [activeProjectId, setActiveProjectId] = useState(null);
  const [tab, setTab] = useState("overview");
  const [allMessages, setAllMessages] = useState({});
  const [input, setInput] = useState("");
  const [loading, setLoading] = useState(true);
  const [err, setErr] = useState("");
  const [sending, setSending] = useState(false);

  // Load projects on mount
  useEffect(() => {
    let alive = true;
    (async () => {
      try {
        const list = await window.clientApi("GET", "/api/portal/projects");
        if (!alive) return;
        const mapped = list.map(apiProjectToUi);
        setAllProjects(mapped);
        if (mapped.length && !activeProjectId) setActiveProjectId(mapped[0].id);
      } catch (e) {
        if (alive) setErr(e.message);
      } finally { if (alive) setLoading(false); }
    })();
    return () => { alive = false; };
  }, []);

  // Load messages for active project
  useEffect(() => {
    if (!activeProjectId) return;
    let alive = true;
    (async () => {
      try {
        const list = await window.clientApi("GET", "/api/portal/messages/" + activeProjectId);
        if (!alive) return;
        setAllMessages(prev => ({ ...prev, [activeProjectId]: list.map(apiMsgToUi) }));
      } catch (e) {}
    })();
    return () => { alive = false; };
  }, [activeProjectId]);

  const project = allProjects.find(p => p.id === activeProjectId) || allProjects[0];
  const messages = (allMessages[activeProjectId] || []);
  const unreadCounts = {};

  if (loading) return <div style={{ padding: 60, textAlign: "center", color: "var(--text-mute)" }}>Loading your projects…</div>;
  if (err) return <div style={{ padding: 60, textAlign: "center" }}>
    <div style={{ color: "#c04030", marginBottom: 16 }}>{err}</div>
    <button onClick={onLogout} className="btn btn-ghost">Sign out</button>
  </div>;
  if (!project) {
    return <div style={{ padding: 60, textAlign: "center" }}>
      <h2 style={{ marginBottom: 12 }}>No projects yet</h2>
      <p style={{ color: "var(--text-soft)", marginBottom: 20 }}>You don't have any active projects assigned. Samuel will create one when your engagement starts.</p>
      <button onClick={onLogout} className="btn btn-ghost">Sign out</button>
    </div>;
  }

  const send = async () => {
    if (!input.trim() || sending) return;
    const text = input.trim();
    setInput("");
    setSending(true);
    // Optimistic UI
    const optimistic = { from: "You", time: "Just now", text };
    setAllMessages(prev => ({ ...prev, [activeProjectId]: [optimistic, ...messages] }));
    try {
      await window.clientApi("POST", "/api/portal/messages/" + activeProjectId, { content: text });
      // Refresh messages
      const list = await window.clientApi("GET", "/api/portal/messages/" + activeProjectId);
      setAllMessages(prev => ({ ...prev, [activeProjectId]: list.map(apiMsgToUi) }));
    } catch (e) {
      alert("Failed to send: " + e.message);
    } finally { setSending(false); }
  };

  return (
    <div className="portal-root">
      <header className="portal-topbar">
        <div className="portal-brand">
          <img src="/assets/logo-mark.png" alt="Samuel Marndi" />
          <div>
            <div className="portal-brand-name">Samuel Marndi</div>
            <div className="portal-brand-sub">Client Portal</div>
          </div>
        </div>
        <div className="portal-topbar-right">
          <a href={`https://wa.me/${CONTACT.whatsapp}`} target="_blank" rel="noopener" className="portal-quick-link">
            <Icons.whatsapp size={14} style={{ color: "#25D366" }} /> WhatsApp Samuel
          </a>
          <a href={`tel:${CONTACT.phoneRaw}`} className="portal-quick-link">
            <Icons.phone size={14} /> Call
          </a>
          <div className="portal-user">
            <div className="portal-avatar">{((user?.name || "Client").split(" ").map(p => p[0]).join("") || "C").slice(0, 2).toUpperCase()}</div>
            <div style={{ display: "flex", flexDirection: "column", lineHeight: 1.2 }}>
              <span style={{ fontSize: 13, fontWeight: 600 }}>{user?.name || "Client"}</span>
              <span style={{ fontSize: 11, color: "var(--admin-mute)" }}>{user?.company || user?.email || "client portal"}</span>
            </div>
          </div>
          {onLogout && (
            <button onClick={onLogout} className="portal-quick-link" title="Sign out" style={{ marginLeft: 4 }}>
              <Icons.close size={14} /> Sign out
            </button>
          )}
        </div>
      </header>

      <div className="portal-shell">
        <aside className="portal-sidebar">
          <div className="portal-sidebar-head">
            <span style={{ fontFamily: "var(--font-mono)", fontSize: 10, letterSpacing: "0.18em", textTransform: "uppercase", color: "var(--admin-mute)" }}>Your projects</span>
            <span style={{ fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--admin-mute)" }}>{allProjects.length}</span>
          </div>
          <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
            {allProjects.map(p => (
              <button key={p.id}
                className={`portal-project-card ${p.id === activeProjectId ? "active" : ""}`}
                onClick={() => { setActiveProjectId(p.id); setTab("overview"); }}>
                <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 8 }}>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontWeight: 600, fontSize: 14, color: "var(--admin-text)", marginBottom: 2 }}>{p.name}</div>
                    <div style={{ fontSize: 11, color: "var(--admin-mute)" }}>{p.service}</div>
                  </div>
                  {(unreadCounts[p.id] || 0) > 0 && <span className="portal-unread">{unreadCounts[p.id]}</span>}
                </div>
                <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8 }}>
                  <span className={`portal-status-dot ${p.health}`}></span>
                  <span style={{ fontSize: 11, color: "var(--admin-text-soft)" }}>{p.status}</span>
                </div>
                {!p.isAMC && (
                  <div style={{ height: 4, background: "var(--admin-line)", borderRadius: 2, overflow: "hidden" }}>
                    <div style={{ width: `${p.progress}%`, height: "100%", background: "var(--admin-copper)" }}></div>
                  </div>
                )}
              </button>
            ))}
          </div>

          <div className="portal-sidebar-foot">
            <a href={"/contact"} className="portal-foot-link">
              <Icons.attach size={14} /> Start a new project
            </a>
            <a href={"/"} className="portal-foot-link">
              <Icons.arrow size={12} style={{ transform: "rotate(180deg)" }} /> Back to site
            </a>
          </div>
        </aside>

        <main className="portal-main">
          <div className="portal-project-head">
            <div>
              <div className="eyebrow" style={{ marginBottom: 12 }}>{project.service}</div>
              <h1 style={{ fontFamily: "var(--font-display)", fontSize: 32, lineHeight: 1.1, marginBottom: 10 }}>{project.name}</h1>
              <div style={{ display: "flex", gap: 18, fontSize: 13, color: "var(--admin-text-soft)", flexWrap: "wrap" }}>
                <span style={{ display: "flex", alignItems: "center", gap: 6 }}>
                  <span className={`portal-status-dot ${project.health}`}></span>
                  <strong>{project.status}</strong>
                </span>
                <span><Icons.user size={12} style={{ marginRight: 4, verticalAlign: "middle" }} /> POC: {project.owner}</span>
                <span><Icons.rupee size={12} style={{ marginRight: 4, verticalAlign: "middle" }} /> {project.value}</span>
                <span><Icons.calendar size={12} style={{ marginRight: 4, verticalAlign: "middle" }} /> {project.started} → {project.deadline}</span>
              </div>
            </div>
            <div className="portal-progress-ring">
              <svg width="84" height="84" viewBox="0 0 100 100">
                <circle cx="50" cy="50" r="40" fill="none" stroke="var(--admin-line)" strokeWidth="8" />
                <circle cx="50" cy="50" r="40" fill="none" stroke="var(--admin-copper)" strokeWidth="8"
                  strokeDasharray={`${project.progress * 2.51} 251`}
                  strokeLinecap="round"
                  transform="rotate(-90 50 50)" />
              </svg>
              <div className="portal-progress-text">
                <div className="portal-progress-num">{project.progress}%</div>
                <div className="portal-progress-lbl">complete</div>
              </div>
            </div>
          </div>

          <div className="admin-tabs" style={{ marginTop: 24, marginBottom: 18 }}>
            {["overview", "timeline", "messages", "files", "invoices"].map(t => (
              <button key={t} className={`admin-tab ${tab === t ? "active" : ""}`} onClick={() => setTab(t)}>
                {t === "messages" ? <><Icons.chat size={12} style={{ marginRight: 4, verticalAlign: "middle" }} /> Chat with Samuel</> : t.charAt(0).toUpperCase() + t.slice(1)}
                {t === "messages" && (unreadCounts[activeProjectId] || 0) > 0 && (
                  <span style={{ marginLeft: 6, padding: "1px 6px", background: "var(--admin-copper)", color: "#fff", borderRadius: 999, fontSize: 10, fontWeight: 700 }}>{unreadCounts[activeProjectId]}</span>
                )}
              </button>
            ))}
          </div>

          {tab === "overview" && <PortalOverview project={project} messages={messages} onSwitchToMessages={() => setTab("messages")} />}
          {tab === "timeline" && <PortalTimeline project={project} />}
          {tab === "messages" && <PortalMessages project={project} messages={messages} input={input} setInput={setInput} onSend={send} />}
          {tab === "files" && <PortalFiles project={project} />}
          {tab === "invoices" && <PortalInvoices project={project} />}
        </main>
      </div>
    </div>
  );
}

function PortalOverview({ project, messages, onSwitchToMessages }) {
  const remaining = (project.milestones || []).filter(m => !m.done).length;
  const done = (project.milestones || []).filter(m => m.done).length;
  const recentMessages = messages.slice(0, 3);

  return (
    <div className="admin-grid-2">
      <AdminCard title="Next up">
        <div style={{ padding: "8px 0" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 14 }}>
            <div style={{ width: 40, height: 40, borderRadius: 10, background: "var(--admin-copper)", display: "grid", placeItems: "center", color: "#fff" }}>
              <Icons.zap size={20} />
            </div>
            <div>
              <div style={{ fontSize: 14, fontWeight: 600 }}>{project.nextMilestone || "—"}</div>
              <div style={{ fontSize: 12, color: "var(--admin-mute)" }}>{project.nextDate || "Target date pending"}</div>
            </div>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
            <div className="admin-mini-stat">
              <div className="admin-mini-num">{done}</div>
              <div className="admin-mini-lbl">milestones done</div>
            </div>
            <div className="admin-mini-stat">
              <div className="admin-mini-num">{remaining}</div>
              <div className="admin-mini-lbl">remaining</div>
            </div>
          </div>
          <button onClick={onSwitchToMessages} className="admin-btn" style={{ width: "100%", marginTop: 14, justifyContent: "center" }}>
            <Icons.chat size={13} /> Message Samuel about this project
          </button>
        </div>
      </AdminCard>
      <AdminCard title="Recent chat" actionLabel="Open chat →">
        <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
          {recentMessages.length === 0 && (
            <div style={{ padding: 24, textAlign: "center", color: "var(--admin-mute)", fontSize: 13 }}>
              No messages yet. <a onClick={onSwitchToMessages} style={{ color: "var(--admin-copper)", cursor: "pointer" }}>Start a conversation →</a>
            </div>
          )}
          {recentMessages.map((m, i) => (
            <div key={i} style={{ padding: 10, background: m.from === "You" ? "color-mix(in srgb, var(--admin-copper) 8%, transparent)" : "var(--admin-bg-3)", borderRadius: 8, borderLeft: m.from === "You" ? "3px solid var(--admin-copper)" : "3px solid var(--admin-mute)" }}>
              <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 4 }}>
                <strong style={{ fontSize: 12 }}>{m.from}</strong>
                <span style={{ fontSize: 11, color: "var(--admin-mute)" }}>{m.time}</span>
              </div>
              <div style={{ fontSize: 13, color: "var(--admin-text-soft)", lineHeight: 1.5, overflow: "hidden", display: "-webkit-box", WebkitLineClamp: 2, WebkitBoxOrient: "vertical" }}>{m.text}</div>
            </div>
          ))}
        </div>
      </AdminCard>
    </div>
  );
}

function PortalTimeline({ project }) {
  const milestones = project.milestones || [];
  if (milestones.length === 0) {
    return (
      <AdminCard title="Active retainer · no scheduled milestones">
        <div style={{ padding: 24, textAlign: "center", color: "var(--admin-mute)", fontSize: 13 }}>
          This is an ongoing AMC retainer. Quarterly reviews and ad-hoc work are tracked through messages and invoices.
        </div>
      </AdminCard>
    );
  }
  return (
    <AdminCard title="Project timeline">
      <div className="portal-timeline">
        {milestones.map((m, i) => (
          <div key={i} className={`portal-ms ${m.done ? "done" : ""} ${m.current ? "current" : ""}`}>
            <div className="portal-ms-dot">
              {m.done ? <Icons.check size={12} /> : (m.current ? <span style={{ width: 6, height: 6, background: "#fff", borderRadius: "50%" }}></span> : null)}
            </div>
            <div className="portal-ms-body">
              <div className="portal-ms-stage">{m.stage}</div>
              <div className="portal-ms-date">{m.date}{m.current && " · in progress"}</div>
            </div>
          </div>
        ))}
      </div>
    </AdminCard>
  );
}

function PortalMessages({ project, messages, input, setInput, onSend }) {
  return (
    <AdminCard noPad>
      <div className="portal-chat-head">
        <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
          <div className="admin-avatar" style={{ width: 40, height: 40 }}>SM</div>
          <div>
            <div style={{ fontSize: 14, fontWeight: 600 }}>Samuel Marndi</div>
            <div style={{ fontSize: 11, color: "var(--admin-mute)", display: "flex", alignItems: "center", gap: 6 }}>
              <span style={{ width: 6, height: 6, borderRadius: "50%", background: "#7EB87A" }}></span>
              Online · typically replies in under 1h
            </div>
          </div>
        </div>
        <div style={{ display: "flex", gap: 8 }}>
          <a href={`https://wa.me/${CONTACT.whatsapp}`} target="_blank" rel="noopener" className="admin-btn admin-btn-ghost admin-btn-sm"><Icons.whatsapp size={12} style={{ color: "#25D366" }} /> WhatsApp</a>
          <a href={`tel:${CONTACT.phoneRaw}`} className="admin-btn admin-btn-ghost admin-btn-sm"><Icons.phone size={12} /> Call</a>
        </div>
      </div>
      <div style={{ padding: "10px 18px", background: "color-mix(in srgb, var(--admin-copper) 6%, var(--admin-bg-2))", borderBottom: "1px solid var(--admin-line)", fontSize: 12, color: "var(--admin-text-soft)" }}>
        💬 This thread is scoped to <strong style={{ color: "var(--admin-text)" }}>{project.name}</strong>. Samuel sees it instantly — typical reply under an hour during business hours.
      </div>
      <div className="portal-chat-body">
        {messages.length === 0 && (
          <div style={{ alignSelf: "center", padding: "40px 20px", textAlign: "center", color: "var(--admin-mute)" }}>
            <Icons.chat size={32} style={{ opacity: 0.4, marginBottom: 12 }} />
            <div style={{ fontSize: 14 }}>No messages yet for this project.</div>
            <div style={{ fontSize: 12, marginTop: 4 }}>Start the conversation below.</div>
          </div>
        )}
        {messages.map((m, i) => (
          <div key={i} className={`portal-msg ${m.from === "You" ? "out" : "in"}`}>
            <div className="portal-msg-meta">
              <strong>{m.from}</strong><span style={{ color: "var(--admin-mute)" }}> · {m.time}</span>
            </div>
            <div className="portal-msg-text">{m.text}</div>
          </div>
        ))}
      </div>
      <div className="portal-chat-input">
        <textarea value={input} onChange={e => setInput(e.target.value)}
          onKeyDown={e => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); onSend(); }}}
          placeholder={`Message Samuel about "${project.name}"… (he gets it instantly, replies on his phone)`}
          rows={2} />
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 8 }}>
          <button className="admin-btn admin-btn-ghost admin-btn-sm"><Icons.attach size={12} /> Attach file</button>
          <button onClick={onSend} className="admin-btn" disabled={!input.trim()}><Icons.send size={12} /> Send</button>
        </div>
      </div>
    </AdminCard>
  );
}

function fmtBytes(n) {
  if (!n) return "—";
  if (n < 1024) return n + " B";
  if (n < 1024 * 1024) return Math.round(n / 1024) + " KB";
  return (n / (1024 * 1024)).toFixed(1) + " MB";
}

function PortalFiles({ project }) {
  const [files, setFiles] = useState([]);
  const [loading, setLoading] = useState(true);
  const [err, setErr] = useState("");
  const [tick, setTick] = useState(0);
  const [uploading, setUploading] = useState(false);
  const [dragOver, setDragOver] = useState(false);

  useEffect(() => {
    if (!project?.id) return;
    let alive = true;
    setLoading(true); setErr("");
    (async () => {
      try {
        const list = await window.clientApi("GET", "/api/files/portal/" + project.id);
        if (alive) setFiles(list || []);
      } catch (e) { if (alive) setErr(e.message); }
      finally { if (alive) setLoading(false); }
    })();
    return () => { alive = false; };
  }, [project?.id, tick]);

  const doUpload = async (fileList) => {
    if (!fileList || fileList.length === 0) return;
    const form = new FormData();
    for (const f of fileList) form.append("files", f);
    setUploading(true); setErr("");
    try {
      const t = (function(){ try { return localStorage.getItem("sm_client_token") || ""; } catch (e) { return ""; }})();
      const r = await fetch("/api/files/portal/" + project.id, {
        method: "POST", headers: { Authorization: "Bearer " + t }, body: form,
      });
      if (!r.ok) {
        const e = await r.json().catch(() => ({}));
        throw new Error(e.error || "Upload failed (" + r.status + ")");
      }
      setTick(t => t + 1);
    } catch (e) { setErr(e.message); }
    finally { setUploading(false); }
  };

  const onDrop = (e) => {
    e.preventDefault(); setDragOver(false);
    doUpload(e.dataTransfer.files);
  };
  const onPick = (e) => doUpload(e.target.files);

  const remove = async (id) => {
    if (!confirm("Delete this file?")) return;
    try { await window.clientApi("DELETE", "/api/files/portal/" + project.id + "/" + id); setTick(t => t + 1); }
    catch (e) { alert("Failed: " + e.message); }
  };

  const myFiles = files.filter(f => /client/i.test(f.uploaded_by || ""));

  return (
    <AdminCard title={"Shared files & documents (" + files.length + ")"} noPad>
      <div
        onDragOver={e => { e.preventDefault(); setDragOver(true); }}
        onDragLeave={() => setDragOver(false)}
        onDrop={onDrop}
        style={{
          margin: 16, padding: 20,
          border: "2px dashed " + (dragOver ? "var(--admin-copper)" : "var(--admin-line)"),
          background: dragOver ? "color-mix(in srgb, var(--admin-copper) 6%, transparent)" : "var(--admin-bg-3)",
          borderRadius: 12,
          textAlign: "center",
          transition: "all 0.15s",
        }}
      >
        <Icons.download size={22} style={{ color: "var(--admin-copper)", transform: "rotate(180deg)", marginBottom: 8 }} />
        <div style={{ fontSize: 14, fontWeight: 600, marginBottom: 6 }}>
          {uploading ? "Uploading…" : "Drop files here, or click to browse"}
        </div>
        <div style={{ fontSize: 11, color: "var(--admin-mute)" }}>Max 5 files per upload · 30 MB each</div>
        <label style={{ display: "inline-block", marginTop: 12 }}>
          <input type="file" multiple onChange={onPick} disabled={uploading} style={{ display: "none" }} />
          <span className="admin-btn admin-btn-sm">{uploading ? "Uploading…" : "Choose files"}</span>
        </label>
      </div>

      {err && <div style={{ margin: "0 16px 12px", padding: "10px 14px", background: "rgba(220,80,60,0.08)", border: "1px solid rgba(220,80,60,0.3)", borderRadius: 10, color: "#c04030", fontSize: 13 }}>{err}</div>}

      {loading && <div style={{ padding: 32, textAlign: "center", color: "var(--admin-mute)" }}>Loading files…</div>}

      {!loading && files.length === 0 && (
        <div style={{ padding: 32, textAlign: "center", color: "var(--admin-mute)", fontSize: 13 }}>
          No files shared yet. Upload something above.
        </div>
      )}

      {files.length > 0 && (
        <table className="admin-table">
          <thead><tr><th></th><th>Name</th><th>Type</th><th>Size</th><th>Uploaded by</th><th>Date</th><th></th></tr></thead>
          <tbody>
            {files.map(f => (
              <tr key={f.id}>
                <td><Icons.download size={14} style={{ color: "var(--admin-copper)" }} /></td>
                <td style={{ fontWeight: 500 }}>{f.name}</td>
                <td><span className="admin-source-tag">{(f.file_type || "file").toUpperCase()}</span></td>
                <td style={{ color: "var(--admin-mute)", fontFamily: "var(--font-mono)", fontSize: 12 }}>{fmtBytes(f.size_bytes)}</td>
                <td style={{ fontSize: 12 }}>{/client/i.test(f.uploaded_by || "") ? "You" : "Samuel"}</td>
                <td style={{ color: "var(--admin-mute)", fontSize: 12 }}>{new Date(f.created_at).toLocaleDateString("en-IN", { month: "short", day: "numeric" })}</td>
                <td>
                  <div style={{ display: "flex", gap: 4 }}>
                    <a href={f.url} target="_blank" rel="noopener" className="admin-btn admin-btn-sm admin-btn-ghost" title="Download"><Icons.download size={12} /></a>
                    {myFiles.some(m => m.id === f.id) && (
                      <button onClick={() => remove(f.id)} className="admin-btn admin-btn-sm admin-btn-ghost" style={{ color: "#E11D48" }} title="Delete">×</button>
                    )}
                  </div>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      )}
    </AdminCard>
  );
}

function PortalInvoices({ project }) {
  const [list, setList] = useState([]);
  const [loading, setLoading] = useState(true);
  const [err, setErr] = useState("");
  const [tick, setTick] = useState(0);

  useEffect(() => {
    if (!project?.id) return;
    let alive = true;
    setLoading(true); setErr("");
    (async () => {
      try {
        // Fetch all client invoices (server scopes to client), then filter by project
        const all = await window.clientApi("GET", "/api/portal/invoices");
        if (alive) setList((all || []).filter(i => !i.project_id || i.project_id === project.id));
      } catch (e) { if (alive) setErr(e.message); }
      finally { if (alive) setLoading(false); }
    })();
    return () => { alive = false; };
  }, [project?.id, tick]);

  const statusUI = (s) => {
    if (s === "paid")      return { label: "Paid",      stage: "won" };
    if (s === "sent")      return { label: "Pending",   stage: "qualified" };
    if (s === "overdue")   return { label: "Overdue",   stage: "new" };
    if (s === "draft")     return { label: "Draft",     stage: "engaged" };
    if (s === "cancelled") return { label: "Cancelled", stage: "lost" };
    return { label: s, stage: "engaged" };
  };
  const fmt = (d) => d ? new Date(d).toLocaleDateString("en-IN", { month: "short", day: "numeric", year: "numeric" }) : "—";

  const totals = list.reduce((acc, i) => {
    const a = parseFloat(i.amount) || 0;
    if (i.status === "paid") acc.paid += a;
    else if (i.status === "sent" || i.status === "overdue") acc.outstanding += a;
    return acc;
  }, { paid: 0, outstanding: 0 });

  return (
    <AdminCard title={"Invoices & payments (" + list.length + ")"} actionLabel={null} noPad>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, padding: 16 }}>
        <div style={{ padding: 14, background: "var(--admin-bg-3)", borderRadius: 8 }}>
          <div style={{ fontSize: 11, color: "var(--admin-mute)", letterSpacing: "0.06em", textTransform: "uppercase", marginBottom: 6 }}>Paid</div>
          <div style={{ fontFamily: "var(--font-display)", fontSize: 28, color: "#059669" }}>₹{totals.paid.toLocaleString("en-IN")}</div>
        </div>
        <div style={{ padding: 14, background: "color-mix(in srgb, var(--admin-copper) 12%, var(--admin-bg-3))", borderRadius: 8 }}>
          <div style={{ fontSize: 11, color: "var(--admin-copper)", letterSpacing: "0.06em", textTransform: "uppercase", marginBottom: 6 }}>Outstanding</div>
          <div style={{ fontFamily: "var(--font-display)", fontSize: 28, color: "var(--admin-copper)" }}>₹{totals.outstanding.toLocaleString("en-IN")}</div>
        </div>
      </div>

      {err && <div style={{ margin: "0 16px 12px", padding: 10, background: "rgba(220,80,60,0.08)", border: "1px solid rgba(220,80,60,0.3)", borderRadius: 8, color: "#c04030", fontSize: 13 }}>{err}</div>}

      {loading && <div style={{ padding: 32, textAlign: "center", color: "var(--admin-mute)" }}>Loading…</div>}

      {!loading && list.length === 0 ? (
        <div style={{ padding: 32, textAlign: "center", color: "var(--admin-mute)", fontSize: 13 }}>No invoices yet for this project.</div>
      ) : list.length > 0 && (
        <table className="admin-table">
          <thead><tr><th>#</th><th>Description</th><th>Amount</th><th>Status</th><th>Issued</th><th>Due / Paid</th><th></th></tr></thead>
          <tbody>
            {list.map((iv) => {
              const ui = statusUI(iv.status);
              return (
                <tr key={iv.id}>
                  <td style={{ fontFamily: "var(--font-mono)", fontSize: 12, color: "var(--admin-copper)" }}>
                    INV-{String(iv.id || "").slice(0, 8).toUpperCase()}
                  </td>
                  <td style={{ fontSize: 13 }}>{iv.description || "—"}</td>
                  <td style={{ fontWeight: 600 }}>₹{Number(iv.amount).toLocaleString("en-IN")}</td>
                  <td><span className="admin-stage-pill" data-stage={ui.stage}>{ui.label}</span></td>
                  <td style={{ color: "var(--admin-mute)", fontSize: 12 }}>{fmt(iv.created_at)}</td>
                  <td style={{ color: iv.status === "overdue" ? "#E11D48" : "var(--admin-mute)", fontSize: 12 }}>
                    {iv.status === "paid" ? fmt(iv.paid_date) : (iv.due_date ? "Due " + fmt(iv.due_date) : "—")}
                  </td>
                  <td>
                    {(iv.status === "sent" || iv.status === "overdue") && iv.public_token && (
                      <a href={"/pay/" + iv.public_token} target="_blank" rel="noopener" className="admin-btn admin-btn-sm">Pay now →</a>
                    )}
                    {iv.status === "paid" && (
                      <span style={{ fontSize: 11, color: "#059669", fontWeight: 600 }}>✓ Paid</span>
                    )}
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      )}
    </AdminCard>
  );
}

window.ClientPortal = ClientPortal;
