/* =============================================================
   site-detail.jsx — Full site drill-in side panel
   Shows: identity, all underlay links, overlay tasks, and chat
   ============================================================= */
function SiteDetail({ siteRef, model, open, onClose, onOpenInPM, role }) {
  if (!siteRef) return null;
  // GA-07: default every model array so an undefined array can never throw
  // (e.g. `model.sites.find(...)`/`underlay.filter(...)`) and blank the panel.
  // Log when model or an expected array is null/undefined so the silent default
  // doesn't mask a real data-loss bug (parallel to PmEditor pick() warn).
  if (!model || !model.underlay || !model.overlay || !model.isp || !model.newUnderlay || !model.sites) {
    console.warn("[SiteDetail] model array(s) missing; rendering with empty defaults");
  }
  const { underlay = [], overlay = [], isp = [], newUnderlay = [], sites = [] } = model || {};

  // Resolve the site object from a partial reference
  const site = React.useMemo(() => {
    if (!siteRef.site) return null;
    const s = siteRef.site;
    // Find full site from model by code or by name
    const full = sites.find((x) =>
      (s.siteCode && x.siteCode === s.siteCode) ||
      (s.siteName && x.siteName && x.siteName.toLowerCase() === s.siteName.toLowerCase()) ||
      (s.id && x.id === s.id)
    );
    return full || s;
  }, [siteRef, model]);

  const focusedLink = siteRef.link;
  const focusedIsp = siteRef.isp;
  const canEdit = role === "engineer" || role === "po" || role === "admin";

  const [tab, setTab] = React.useState("summary");

  // GA-11: a notification deep-link can request this site's Discussion tab.
  React.useEffect(() => {
    if (siteRef && siteRef.openDiscussion) setTab("chat");
  }, [siteRef]);

  if (!site) return null;

  const links = site.links || underlay.filter((u) => u.siteName && site.siteName && u.siteName.toLowerCase() === site.siteName.toLowerCase());
  const ov = site.overlay || overlay.find((o) =>
    (site.siteCode && o.siteCode === site.siteCode) ||
    (site.siteName && o.siteName && o.siteName.toLowerCase() === site.siteName.toLowerCase())
  );
  const sameText = (a, b) => a && b && String(a).toLowerCase() === String(b).toLowerCase();
  // PMNU-04: resolve linked New Underlay rows through the shared site -> New Underlay
  // resolver so multi-link sites stay isolated by Link Ref (no merge-by-site-code).
  const resolvedNewUnderlay = PRData.resolveSiteNewUnderlay
    ? PRData.resolveSiteNewUnderlay(
        focusedLink && focusedLink.linkRef
          ? { ...site, linkRefs: [...(site.linkRefs || []), focusedLink.linkRef] }
          : site,
        model
      )
    : { rows: [], blocks: [], diagnostics: { noMatch: true, duplicateLinkRefs: [], ambiguousFallback: [], orphans: [] } };
  const newUnderlayRows = resolvedNewUnderlay.rows;
  const newUnderlayDiag = resolvedNewUnderlay.diagnostics;
  const ispRows = isp.filter((r) =>
    (focusedIsp && r._id === focusedIsp._id) ||
    (focusedLink && r.linkRef && r.linkRef === focusedLink.linkRef) ||
    (site.siteCode && r.siteCode && r.siteCode === site.siteCode) ||
    (site.siteName && sameText(r.siteName, site.siteName))
  );

  const activated = links.filter((u) => u.circuitStatus.toLowerCase() === "activated").length;
  const atRisk = links.filter((u) => (u.atRisk || "").toLowerCase() === "yes").length;
  const blocked = links.filter((u) => u.blocker).length;
  const lifecycle = site.lifecycle || (PRData.siteLifecycle ? PRData.siteLifecycle(site, model) : null);
  const openPM = (entity, idRef) => {
    if (!onOpenInPM || !entity) return;
    onClose && onClose();
    onOpenInPM(entity, idRef);
  };

  return (
    <SidePanel
      open={open}
      onClose={onClose}
      title={site.siteName || site.id || "Site detail"}
      subtitle={
        <span style={{display:"flex", gap: 8, alignItems: "center"}}>
          {site.siteCode && <span className="mono" style={{fontSize: 11}}>{site.siteCode}</span>}
          <span>{window.COUNTRY_ISO.canonical(site.country) || "—"} · {site.region || "—"}</span>
        </span>
      }
    >
      <div style={{display: "flex", gap: 6, flexWrap: "wrap", marginBottom: 14}}>
        <CriticalityPill value={site.criticality}/>
        {site.classification && <Pill kind="info">{site.classification}</Pill>}
        {site.techno && <Pill kind="muted">{site.techno}</Pill>}
        {site.siteStatus && site.siteStatus !== "Active" && <Pill kind="warn">{site.siteStatus}</Pill>}
        {ov && ov.migrationStatus === "Migrated" && <Pill kind="ok">Migrated</Pill>}
        {site.lifecycle && window.LifecyclePhasePill && <LifecyclePhasePill phase={site.lifecycle.phase} percent={site.lifecycle.percent} compact/>}
      </div>

      {(() => {
        if (!PRData.overlayEligibility) return null;
        const elig = PRData.overlayEligibility(site, model, {});
        if (!elig) return null;
        if (elig.eligible) {
          return (
            <div className="pm-readonly-pill" style={{ marginBottom: 12, background: "var(--ok-soft)", color: "var(--ok)" }}>
              <strong>Overlay eligible.</strong> Underlay activated for all required links — site can move to overlay migration.
            </div>
          );
        }
        return (
          <div className="pm-readonly-pill" style={{ marginBottom: 12, background: "var(--warn-soft)", color: "var(--warn)" }}>
            <strong>Overlay not eligible yet.</strong>
            <ul style={{ margin: "4px 0 0 16px", padding: 0, fontSize: 12 }}>
              {(elig.blockers || []).slice(0, 4).map((b, i) => (
                <li key={i}>{b.message || b.code}{b.ref ? <span className="mono"> · {b.ref}</span> : null}</li>
              ))}
            </ul>
          </div>
        );
      })()}

      <div className="grid g-3" style={{gap: 8, marginBottom: 16}}>
        <Mini label="Links" value={links.length}/>
        <Mini label="Activated" value={activated} tone={activated === links.length && links.length ? "ok" : undefined}/>
        <Mini label="At risk" value={atRisk + blocked} tone={atRisk + blocked ? "risk" : undefined}/>
      </div>

      <SiteLifecycleHandoff
        site={site}
        lifecycle={lifecycle}
        links={links}
        newUnderlayRows={newUnderlayRows}
        overlay={ov}
        ispRows={ispRows}
        canEdit={canEdit}
        openPM={openPM}
      />

      <Tabs value={tab} onChange={setTab} options={[
        { value: "summary", label: "Summary" },
        { value: "underlay", label: "Underlay", count: links.length },
        { value: "overlay", label: "Overlay tasks", count: ov ? 1 : 0 },
        { value: "newUnderlay", label: "New underlay", count: newUnderlayRows.length },
        { value: "isp", label: "ISP", count: ispRows.length },
        { value: "raw", label: "All fields" },
        { value: "chat", label: "Discussion" },
      ]}/>

      {tab === "summary" && (
        <>
          <div className="side-panel-section">
            <h4>Identity</h4>
            <KVGrid items={[
              { k: "Site name", v: site.siteName },
              { k: "Site code", v: site.siteCode ? <span className="mono">{site.siteCode}</span> : null },
              { k: "Short label", v: site.shortLabel },
              { k: "Country", v: window.COUNTRY_ISO.canonical(site.country) },
              { k: "Region", v: site.region },
              { k: "Management entity", v: site.me },
              { k: "Criticality", v: <CriticalityPill value={site.criticality}/> },
              { k: "Classification", v: site.classification },
              { k: "Site status", v: site.siteStatus },
              { k: "TECHNO", v: site.techno },
            ]}/>
          </div>
          {ov && (
            <div className="side-panel-section">
              <h4>Overlay snapshot</h4>
              <KVGrid items={[
                { k: "Hardware quote", v: <StatusCell value={ov.hwQuote} kind="task"/> },
                { k: "Hardware PO", v: <StatusCell value={ov.hwPO} kind="task"/> },
                { k: "HW delivery", v: <StatusCell value={ov.hwDelivery} kind="task"/> },
                { k: "HW target", v: PRData.fmtDate(ov.hwTarget) !== "—" ? PRData.fmtDate(ov.hwTarget) : null },
                { k: "Migration", v: <StatusCell value={ov.migrationStatus || ov.migrationSchedule} kind="task"/> },
                { k: "Migration date", v: PRData.fmtDate(ov.confirmedMigration) !== "—" ? PRData.fmtDate(ov.confirmedMigration) : null },
                { k: "Migration week", v: ov.migrationWeek },
                { k: "Comment", v: ov.comment },
              ]}/>
            </div>
          )}
          {focusedLink && (
            <div className="side-panel-section">
              <h4>Highlighted link · {focusedLink.linkRef}</h4>
              <LinkDetail link={focusedLink}/>
            </div>
          )}
        </>
      )}

      {tab === "underlay" && (
        <div className="side-panel-section">
          {links.length === 0 ? <Empty title="No underlay links for this site." /> : (
            <div style={{display: "flex", flexDirection: "column", gap: 14}}>
              {links.map((u) => (
                <div key={u._id} style={{
                  border: "1px solid var(--line-soft)", borderRadius: 10,
                  padding: 14, background: "var(--surface)",
                }}>
                  <div style={{display:"flex", justifyContent:"space-between", marginBottom: 10}}>
                    <div>
                      <div style={{fontFamily: "var(--font-display)", fontSize: 16, fontWeight: 600, letterSpacing: "-0.015em"}}>
                        Link {u.linkRef}
                        {u.linkNum && <span style={{marginLeft: 8, color: "var(--ink-500)", fontFamily: "var(--font-mono)", fontSize: 11, fontWeight: 400}}>· {u.linkNum}</span>}
                      </div>
                      <div style={{fontSize: 12, color: "var(--ink-500)"}}>{u.vendor} · {u.batch}</div>
                    </div>
                    <div style={{display:"flex", alignItems:"center", gap: 6, flexWrap: "wrap", justifyContent: "flex-end"}}>
                      <RiskPill level={u.riskLevel} atRisk={u.atRisk}/>
                      {canEdit && onOpenInPM && <button className="btn ghost site-detail-edit-underlay" data-id={u.linkRef || u.siteName} style={{ padding: "2px 8px", fontSize: 11 }} onClick={() => openPM("underlay", u.linkRef || u.siteName)}>Edit underlay link</button>}
                    </div>
                  </div>
                  <LinkDetail link={u}/>
                </div>
              ))}
            </div>
          )}
        </div>
      )}

      {tab === "overlay" && (
        <div className="side-panel-section">
          {!ov ? <Empty title="No overlay record." sub="This site doesn't have an entry in the Overlay tracker." /> : (
            <>
              <div style={{display:"flex", justifyContent:"space-between", gap: 8, alignItems: "center", marginBottom: 8}}>
                <h4 style={{margin: 0}}>Task readiness</h4>
                {canEdit && onOpenInPM && <button className="btn ghost site-detail-edit-overlay" data-id={ov.siteCode || ov.siteName} style={{ padding: "2px 8px", fontSize: 11 }} onClick={() => openPM("overlay", ov.siteCode || ov.siteName)}>Edit overlay site</button>}
              </div>
              <div style={{display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8, marginBottom: 16}}>
                {PRData.OVERLAY_TASK_FIELDS.map((t) => (
                  <div key={t.key} style={{display:"flex", justifyContent:"space-between", padding: "8px 10px", background: "var(--bg-elev)", borderRadius: 8, fontSize: 12}}>
                    <span style={{color: "var(--ink-700)"}}>{t.label}</span>
                    <StatusCell value={ov.tasks[t.key]} kind="task"/>
                  </div>
                ))}
              </div>
              <h4>Hardware & migration</h4>
              <KVGrid items={[
                { k: "HW quotation", v: <StatusCell value={ov.hwQuote} kind="task"/> },
                { k: "HW purchase order", v: <StatusCell value={ov.hwPO} kind="task"/> },
                { k: "HW delivery", v: <StatusCell value={ov.hwDelivery} kind="task"/> },
                { k: "HW delivery target", v: PRData.fmtDate(ov.hwTarget) !== "—" ? PRData.fmtDate(ov.hwTarget) : null },
                { k: "Migration timeline", v: ov.migrationTimeline },
                { k: "Migration schedule", v: <StatusCell value={ov.migrationSchedule} kind="task"/> },
                { k: "Migration status", v: <StatusCell value={ov.migrationStatus} kind="task"/> },
                { k: "Confirmed date", v: PRData.fmtDate(ov.confirmedMigration) !== "—" ? PRData.fmtDate(ov.confirmedMigration) : null },
                { k: "Migration week", v: ov.migrationWeek },
                { k: "Comment", v: ov.comment },
              ]}/>
            </>
          )}
        </div>
      )}

      {tab === "newUnderlay" && (
        <div className="side-panel-section">
          {(() => {
            const diag = newUnderlayDiag || {};
            const notes = [];
            if ((diag.duplicateLinkRefs || []).length) notes.push(`${diag.duplicateLinkRefs.length} duplicate Link Ref${diag.duplicateLinkRefs.length > 1 ? "s" : ""}`);
            if ((diag.ambiguousFallback || []).length) notes.push(`${diag.ambiguousFallback.length} site-code match${diag.ambiguousFallback.length > 1 ? "es" : ""} not auto-linked (own a different Link Ref)`);
            if ((diag.orphans || []).length) notes.push(`${diag.orphans.length} row${diag.orphans.length > 1 ? "s" : ""} without a Link Ref`);
            return notes.length ? (
              <div style={{fontSize: 11, color: "var(--ink-500)", border: "1px solid var(--line-soft)", borderRadius: 8, padding: "6px 10px", marginBottom: 12}}>
                Resolver notes: {notes.join(" · ")}.
              </div>
            ) : null;
          })()}
          {newUnderlayRows.length === 0 ? <Empty title="No new underlay records for this site." /> : (
            <div style={{display: "flex", flexDirection: "column", gap: 14}}>
              {newUnderlayRows.map((u) => (
                <div key={u._id} style={{border: "1px solid var(--line-soft)", borderRadius: 10, padding: 14, background: "var(--surface)"}}>
                  <div style={{display:"flex", justifyContent:"space-between", marginBottom: 10}}>
                    <div>
                      <div style={{fontFamily: "var(--font-display)", fontSize: 16, fontWeight: 600, letterSpacing: "-0.015em", display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap"}}>
                        <span>Link {u.linkRef || "—"}</span>
                        {u.linkAB ? <span style={{fontSize: 11, fontWeight: 600, color: "var(--ink-500)", border: "1px solid var(--line-soft)", borderRadius: 6, padding: "1px 6px"}}>Link {u.linkAB}</span> : null}
                      </div>
                      <div style={{fontSize: 12, color: "var(--ink-500)"}}>{u.partner || u.isp || "—"} · {u.batch || "—"}</div>
                    </div>
                    <div style={{display:"flex", alignItems:"center", gap: 6, flexWrap: "wrap", justifyContent: "flex-end"}}>
                      <StatusCell value={u.circuitDelivered} kind="circuit"/>
                      {canEdit && onOpenInPM && <button className="btn ghost site-detail-edit-readiness" data-id={u.linkRef || u.siteCode || u.siteName} style={{ padding: "2px 8px", fontSize: 11 }} onClick={() => openPM("newUnderlay", u.linkRef || u.siteCode || u.siteName)}>Edit readiness</button>}
                    </div>
                  </div>
                  <NewUnderlayDetail row={u}/>
                </div>
              ))}
            </div>
          )}
        </div>
      )}

      {tab === "isp" && (
        <div className="side-panel-section">
          {ispRows.length === 0 ? <Empty title="No ISP termination records for this site." /> : (
            <div style={{display: "flex", flexDirection: "column", gap: 14}}>
              {ispRows.map((r) => (
                <div key={r._id} style={{border: "1px solid var(--line-soft)", borderRadius: 10, padding: 14, background: "var(--surface)"}}>
                  <div style={{display:"flex", justifyContent:"space-between", marginBottom: 10}}>
                    <div>
                      <div style={{fontFamily: "var(--font-display)", fontSize: 16, fontWeight: 600, letterSpacing: "-0.015em"}}>
                        Legacy link {r.linkRef || r.linkNum || "—"}
                      </div>
                      <div style={{fontSize: 12, color: "var(--ink-500)"}}>{r.isp || "—"} · {r.circuitId || "—"}</div>
                    </div>
                    <StatusCell value={r.terminated || "No"} kind="task"/>
                  </div>
                  <IspDetail row={r}/>
                </div>
              ))}
            </div>
          )}
        </div>
      )}

      {tab === "raw" && (
        <div className="side-panel-section">
          <h4>All workbook fields for this site</h4>
          {ov && (
            <details open style={{marginBottom: 14}}>
              <summary style={{cursor: "pointer", fontSize: 12, fontWeight: 500, color: "var(--ink-700)", marginBottom: 8}}>Overlay row</summary>
              <RawTable raw={ov._raw}/>
            </details>
          )}
          {links.map((u) => (
            <details key={u._id} style={{marginBottom: 14}}>
              <summary style={{cursor: "pointer", fontSize: 12, fontWeight: 500, color: "var(--ink-700)", marginBottom: 8}}>
                Underlay row · {u.linkRef}
              </summary>
              <RawTable raw={u._raw}/>
            </details>
          ))}
          {newUnderlayRows.map((u) => (
            <details key={u._id} style={{marginBottom: 14}}>
              <summary style={{cursor: "pointer", fontSize: 12, fontWeight: 500, color: "var(--ink-700)", marginBottom: 8}}>
                New underlay row · {u.linkRef || u.siteName}
              </summary>
              <RawTable raw={u._raw}/>
            </details>
          ))}
          {ispRows.map((r) => (
            <details key={r._id} style={{marginBottom: 14}}>
              <summary style={{cursor: "pointer", fontSize: 12, fontWeight: 500, color: "var(--ink-700)", marginBottom: 8}}>
                ISP termination row · {r.linkRef || r.siteName}
              </summary>
              <RawTable raw={r._raw}/>
            </details>
          ))}
        </div>
      )}

      {tab === "chat" && (
        <div className="side-panel-section" data-site-discussion>
          {typeof window.PmCommentsRail === "function" ? (
            <window.PmCommentsRail
              entity="site"
              idRef={site.id || site.siteCode || site.siteName}
              highlightCommentId={siteRef.discussionCommentId}
              compact
            />
          ) : (
            <ChatThread
              scopeKind="site"
              scopeId={site.id || site.siteCode || site.siteName}
              title={`Site · ${site.siteName || site.id}`}
            />
          )}
        </div>
      )}
    </SidePanel>
  );
}

function siteDetailRefKey(value) {
  return String(value || "").trim().toUpperCase().replace(/\s+/g, "");
}

function siteDetailPhaseLabel(value) {
  return String(value || "no-data").replace(/-/g, " ");
}

function siteDetailNuPhaseGroups() {
  const fallback = [
    { key: "preChangeReadiness", label: "Pre-Change Readiness", fields: ["rj45", "wanIp", "pdu", "rackSpace", "fortigate", "lconAvail", "hfSupport", "lconOk", "sdOk", "businessOk"] },
    { key: "changeManagement", label: "Change Management", fields: ["imsTask", "cnsTask", "ocdTask", "iamdwTask", "azureTask", "cab"] },
    { key: "linkMigration", label: "Link Migration", fields: ["gateway", "internet", "userTests", "migration", "rackPhoto"] },
    { key: "postMigration", label: "Post Migration", fields: ["servicenow", "confluence", "cmdb", "legacyTerm"] },
  ];
  const configured = (PRData.NEW_UNDERLAY_FIELD_GROUPS || [])
    .filter((group) => ["preChangeReadiness", "changeManagement", "linkMigration", "postMigration"].includes(group.key))
    .map((group) => ({
      key: group.key,
      label: group.label,
      fields: (group.fields || []).map((field) => String(field).replace(/^readiness\./, "")).filter((field) => field && !["changeNo", "changeDate"].includes(field)),
    }))
    .filter((group) => group.fields.length);
  return configured.length ? configured : fallback;
}

function siteDetailReadinessStats(row, keys) {
  const fields = keys || (PRData.NEW_UNDERLAY_READINESS || []).map((field) => field.key);
  let ready = 0;
  let blocked = 0;
  let partial = 0;
  fields.forEach((key) => {
    const value = row && row.readiness ? row.readiness[key] : "";
    const bucket = PRData.statusBucket(value, "task");
    if (bucket === "ok") ready++;
    else if (bucket === "risk") blocked++;
    else if (bucket === "warn") partial++;
  });
  const total = fields.length || 1;
  return { ready, blocked, partial, total, pct: Math.round((ready / total) * 100) };
}

function siteDetailAggregateNuStats(rows) {
  const base = { ready: 0, blocked: 0, partial: 0, total: 0 };
  (rows || []).forEach((row) => {
    const stats = siteDetailReadinessStats(row);
    base.ready += stats.ready;
    base.blocked += stats.blocked;
    base.partial += stats.partial;
    base.total += stats.total;
  });
  base.pct = base.total ? Math.round((base.ready / base.total) * 100) : 0;
  return base;
}

function siteDetailNextAction({ lifecycle, links, newUnderlayRows, overlay, ispRows }) {
  const linkCount = links.length;
  const activated = links.filter((u) => (u.circuitStatus || "").toLowerCase() === "activated").length;
  const blocker = links.find((u) => u.blocker);
  const nuStats = siteDetailAggregateNuStats(newUnderlayRows);
  const overlayStatus = overlay && (overlay.migrationStatus || overlay.migrationSchedule || overlay.hwDelivery);
  const ispOpen = (ispRows || []).filter((row) => row.terminated !== "Yes");
  if (blocker) return `Clear underlay blocker on ${blocker.linkRef || blocker.siteName}.`;
  if (linkCount && activated < linkCount) return `Finish underlay activation for ${linkCount - activated} link${linkCount - activated === 1 ? "" : "s"}.`;
  if (nuStats.blocked) return `Resolve ${nuStats.blocked} blocked New Underlay readiness item${nuStats.blocked === 1 ? "" : "s"}.`;
  if (nuStats.total && nuStats.ready < nuStats.total) return `Complete New Underlay readiness checks before overlay migration.`;
  if (!overlay) return "Create or validate the overlay tracker row for this site.";
  if (overlay.migrationStatus !== "Migrated") return `Move overlay migration forward from ${overlayStatus || "not started"}.`;
  if (ispOpen.length) return `Close ${ispOpen.length} legacy ISP termination item${ispOpen.length === 1 ? "" : "s"}.`;
  if (lifecycle && lifecycle.lifecyclePhase === "complete") return "Site is complete. Keep monitoring for closure evidence.";
  return "Review project state and keep the next workflow owner updated.";
}

function SiteLifecycleHandoff({ site, lifecycle, links, newUnderlayRows, overlay, ispRows, canEdit, openPM }) {
  const activated = links.filter((u) => (u.circuitStatus || "").toLowerCase() === "activated").length;
  const nuStats = siteDetailAggregateNuStats(newUnderlayRows);
  const overlayReady = overlay && (overlay.hwDelivery === "HW Delivered" || overlay.hwDelivery === "Delivered" || overlay.migrationStatus === "Migrated");
  const ispDone = ispRows.length ? ispRows.filter((row) => row.terminated === "Yes").length : 0;
  const underlayByRef = new Map((links || []).map((link) => [siteDetailRefKey(link.linkRef), link]));
  const nextAction = siteDetailNextAction({ lifecycle, links, newUnderlayRows, overlay, ispRows });
  const phase = lifecycle && (lifecycle.lifecyclePhase || lifecycle.phase);
  const percent = lifecycle && (lifecycle.lifecyclePercent ?? lifecycle.percent);
  return (
    <div className="site-life-panel" data-site-lifecycle="true">
      <div className="site-life-head">
        <div>
          <div className="pm-field-label">Operational lifecycle</div>
          <div className="site-life-title">Underlay - New Underlay - Overlay handoff</div>
        </div>
        <div className="site-life-actions">
          {phase && window.LifecyclePhasePill && <LifecyclePhasePill phase={phase} percent={percent} compact/>}
          {canEdit && openPM && <button className="btn ghost site-detail-open-pm" onClick={() => openPM("site", site.siteCode || site.siteName || site.id)}>Open in PM</button>}
        </div>
      </div>
      <div className="site-life-next">
        <span>Next action</span>
        <strong>{nextAction}</strong>
      </div>
      <div className="site-life-flow">
        <SiteLifecycleStage
          label="Underlay links"
          value={`${activated}/${links.length || 0}`}
          hint="activated"
          pct={links.length ? Math.round((activated / links.length) * 100) : 0}
          tone={activated === links.length && links.length ? "ok" : "warn"}
          action={canEdit && openPM && links[0] ? { label: "Edit underlay link", className: "site-detail-edit-underlay", id: links[0].linkRef || links[0].siteName, onClick: () => openPM("underlay", links[0].linkRef || links[0].siteName) } : null}
        />
        <SiteLifecycleStage
          label="New Underlay readiness"
          value={`${nuStats.ready}/${nuStats.total}`}
          hint={`${newUnderlayRows.length} linked rows`}
          pct={nuStats.pct}
          tone={nuStats.blocked ? "risk" : (nuStats.pct === 100 ? "ok" : "warn")}
          action={canEdit && openPM && newUnderlayRows[0] ? { label: "Edit readiness", className: "site-detail-edit-readiness", id: newUnderlayRows[0].linkRef || newUnderlayRows[0].siteName, onClick: () => openPM("newUnderlay", newUnderlayRows[0].linkRef || newUnderlayRows[0].siteName) } : null}
        />
        <SiteLifecycleStage
          label="Overlay migration"
          value={overlay ? (overlay.migrationStatus || overlay.migrationSchedule || overlay.hwDelivery || "Tracked") : "Missing"}
          hint={overlay && overlay.confirmedMigration ? PRData.fmtDate(overlay.confirmedMigration) : "hardware / migration"}
          pct={overlay && overlay.migrationStatus === "Migrated" ? 100 : (overlayReady ? 70 : 20)}
          tone={overlay && overlay.migrationStatus === "Migrated" ? "ok" : "info"}
          action={canEdit && openPM && overlay ? { label: "Edit overlay site", className: "site-detail-edit-overlay", id: overlay.siteCode || overlay.siteName, onClick: () => openPM("overlay", overlay.siteCode || overlay.siteName) } : null}
        />
        <SiteLifecycleStage
          label="ISP termination"
          value={ispRows.length ? `${ispDone}/${ispRows.length}` : "None"}
          hint="legacy closure"
          pct={ispRows.length ? Math.round((ispDone / ispRows.length) * 100) : 100}
          tone={ispRows.length && ispDone < ispRows.length ? "warn" : "ok"}
          action={canEdit && openPM && ispRows[0] ? { label: "Open ISP", className: "site-detail-edit-isp", id: ispRows[0].linkRef || ispRows[0].circuitId || ispRows[0].siteName, onClick: () => openPM("isp", ispRows[0].linkRef || ispRows[0].circuitId || ispRows[0].siteName) } : null}
        />
      </div>
      {newUnderlayRows.length > 0 && (
        <div className="site-life-linkgrid">
          {newUnderlayRows.map((row) => (
            <SiteNewUnderlayLifecycleCard
              key={row._id || row.linkRef}
              row={row}
              underlayLink={underlayByRef.get(siteDetailRefKey(row.linkRef))}
              canEdit={canEdit}
              openPM={openPM}
            />
          ))}
        </div>
      )}
    </div>
  );
}

function SiteLifecycleStage({ label, value, hint, pct, tone, action }) {
  return (
    <div className={"site-life-stage " + (tone || "")}>
      <span>{label}</span>
      <strong>{value}</strong>
      <em>{hint}</em>
      <div className="site-life-bar"><i style={{ width: `${Math.max(0, Math.min(100, pct || 0))}%` }}/></div>
      {action && <button className={"btn ghost " + (action.className || "")} data-id={action.id || ""} onClick={action.onClick}>{action.label}</button>}
    </div>
  );
}

function SiteNewUnderlayLifecycleCard({ row, underlayLink, canEdit, openPM }) {
  const stats = siteDetailReadinessStats(row);
  const groups = siteDetailNuPhaseGroups();
  return (
    <div className="site-life-linkcard" data-new-underlay-phase-progress="true">
      <div className="site-life-linkhead">
        <div>
          <strong className="mono">{row.linkRef || "-"}</strong>
          <span>{row.linkAB || "Link"} - {row.isp || underlayLink && underlayLink.vendor || "-"}</span>
        </div>
        <div className={stats.blocked ? "site-life-blocked" : "site-life-ready"}>{stats.blocked ? `${stats.blocked} blocked` : `${stats.pct}% ready`}</div>
      </div>
      <div className="site-life-phasegrid">
        {groups.map((group) => {
          const phaseStats = siteDetailReadinessStats(row, group.fields);
          return (
            <div key={group.key} className="site-life-phase">
              <span>{group.label}</span>
              <strong>{phaseStats.ready}/{phaseStats.total}</strong>
              <div className="site-life-bar"><i style={{ width: `${phaseStats.pct}%` }}/></div>
              {phaseStats.blocked > 0 && <em>{phaseStats.blocked} blocked</em>}
            </div>
          );
        })}
      </div>
      <div className="site-life-mirror">
        <SiteMirror label="CDD" value={PRData.fmtDate(row.cdd)}/>
        <SiteMirror label="Circuit delivered" value={row.circuitDelivered}/>
        <SiteMirror label="Change No." value={row.changeNo}/>
        <SiteMirror label="Change date" value={PRData.fmtDate(row.changeDate)}/>
      </div>
      {canEdit && openPM && (
        <div className="site-life-card-actions">
          {underlayLink && <button className="btn ghost site-detail-edit-underlay" data-id={underlayLink.linkRef || underlayLink.siteName} onClick={() => openPM("underlay", underlayLink.linkRef || underlayLink.siteName)}>Edit underlay link</button>}
          <button className="btn ghost site-detail-edit-readiness" data-id={row.linkRef || row.siteCode || row.siteName} onClick={() => openPM("newUnderlay", row.linkRef || row.siteCode || row.siteName)}>Edit readiness</button>
        </div>
      )}
    </div>
  );
}

function SiteMirror({ label, value }) {
  const display = value && value !== "â€”" ? value : "-";
  return (
    <div>
      <span>{label}</span>
      <strong>{display}</strong>
      <em>from Underlay</em>
    </div>
  );
}

function Mini({ label, value, tone }) {
  return (
    <div style={{padding: "8px 10px", background: "var(--bg-elev)", borderRadius: 8, border: "1px solid var(--line-soft)"}}>
      <div style={{fontSize: 10, textTransform: "uppercase", letterSpacing: "0.08em", color: "var(--ink-500)"}}>{label}</div>
      <div style={{
        fontFamily: "var(--font-display)",
        fontSize: 20, fontWeight: 600, letterSpacing: "-0.02em",
        color: tone === "ok" ? "var(--ok)" : tone === "risk" ? "var(--risk)" : tone === "warn" ? "var(--warn)" : "var(--ink-900)",
      }}>{value}</div>
    </div>
  );
}

function LinkDetail({ link }) {
  const u = link;
  return (
    <KVGrid items={[
      { k: "Vendor", v: u.vendor && <Pill kind="info">{u.vendor}</Pill> },
      { k: "Batch", v: u.batch },
      { k: "PR order status", v: <StatusCell value={u.prOrderStatus} kind="task"/> },
      { k: "Order placed", v: PRData.fmtDate(u.prOrderDate) !== "—" ? PRData.fmtDate(u.prOrderDate) : null },
      { k: "Order progress", v: typeof u.orderProgress === "number" ? (
        <div style={{display: "flex", alignItems: "center", gap: 8}}>
          <div style={{flex: 1, maxWidth: 100}}><Progress value={u.orderProgress} max={100} thin/></div>
          <span className="mono" style={{fontSize: 11.5}}>{Math.round(u.orderProgress)}%</span>
        </div>
      ) : null },
      { k: "Circuit delivery", v: <StatusCell value={u.circuitStatus} kind="circuit"/> },
      { k: "Circuit date", v: PRData.fmtDate(u.circuitDate) !== "—" ? PRData.fmtDate(u.circuitDate) : null },
      { k: "Planned delivery", v: PRData.fmtDate(u.planned) !== "—" ? PRData.fmtDate(u.planned) : null },
      { k: "CDD", v: PRData.fmtDate(u.cdd) !== "—" ? PRData.fmtDate(u.cdd) : null },
      { k: "Days slipped", v: typeof u.daysSlipped === "number" ? <span className="mono">{u.daysSlipped}d</span> : null },
      { k: "LCON validation", v: u.lconStatus },
      { k: "Provider", v: u.provider || (u.providerStatus ? u.providerStatus : null) },
      { k: "Migration date", v: PRData.fmtDate(u.migrationDate) !== "—" ? PRData.fmtDate(u.migrationDate) : null },
      { k: "Action owner", v: u.owner },
      { k: "Blocker", v: u.blocker ? <span style={{color: "var(--risk)"}}>{u.blocker}</span> : null },
      { k: "Next steps", v: u.nextSteps },
    ].filter((it) => it.v !== null && it.v !== undefined && it.v !== "")}/>
  );
}

function NewUnderlayDetail({ row }) {
  const readyCount = PRData.NEW_UNDERLAY_READINESS.reduce(
    (n, t) => n + (PRData.statusBucket(row.readiness[t.key], "task") === "ok" ? 1 : 0),
    0
  );
  return (
    <>
      <KVGrid items={[
        { k: "Site code", v: row.siteCode ? <span className="mono">{row.siteCode}</span> : null },
        { k: "Country", v: window.COUNTRY_ISO.canonical(row.country) },
        { k: "Region", v: row.region },
        { k: "Management entity", v: row.me },
        { k: "ISP", v: row.isp },
        { k: "Local partner", v: row.partner },
        { k: "Lead", v: row.lead },
        { k: "Order date", v: PRData.fmtDate(row.orderDate) !== "—" ? PRData.fmtDate(row.orderDate) : null },
        { k: "CDD", v: PRData.fmtDate(row.cdd) !== "—" ? PRData.fmtDate(row.cdd) : null },
        { k: "Survey", v: <StatusCell value={row.surveyStatus} kind="task"/> },
        { k: "Circuit delivered", v: <StatusCell value={row.circuitDelivered} kind="circuit"/> },
        { k: "Task completion", v: typeof row.taskPct === "number" ? `${Math.round(row.taskPct)}%` : null },
        { k: "Readiness", v: `${readyCount}/${PRData.NEW_UNDERLAY_READINESS.length} ready` },
      ].filter((it) => it.v !== null && it.v !== undefined && it.v !== "")}/>
      <div style={{display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8, marginTop: 14}}>
        {PRData.NEW_UNDERLAY_READINESS.map((t) => (
          <div key={t.key} style={{display:"flex", justifyContent:"space-between", padding: "8px 10px", background: "var(--bg-elev)", borderRadius: 8, fontSize: 12}}>
            <span style={{color: "var(--ink-700)"}}>{t.label}</span>
            <StatusCell value={row.readiness[t.key]} kind="task"/>
          </div>
        ))}
      </div>
    </>
  );
}

function IspDetail({ row }) {
  return (
    <KVGrid items={[
      { k: "Legacy ref", v: row.linkRef ? <span className="mono">{row.linkRef}</span> : null },
      { k: "Link number", v: row.linkNum ? <span className="mono">{row.linkNum}</span> : null },
      { k: "Site code", v: row.siteCode ? <span className="mono">{row.siteCode}</span> : null },
      { k: "Country", v: window.COUNTRY_ISO.canonical(row.country) },
      { k: "Region", v: row.region },
      { k: "Batch", v: row.batch },
      { k: "ISP", v: row.isp },
      { k: "Circuit ID", v: row.circuitId ? <span className="mono">{row.circuitId}</span> : null },
      { k: "Lead", v: row.lead },
      { k: "Contact", v: row.contact },
      { k: "Request date", v: PRData.fmtDate(row.requestDate) !== "—" ? PRData.fmtDate(row.requestDate) : null },
      { k: "Termination date", v: PRData.fmtDate(row.terminationDate) !== "—" ? PRData.fmtDate(row.terminationDate) : null },
      { k: "Terminated", v: <StatusCell value={row.terminated || "No"} kind="task"/> },
      { k: "Final billing", v: PRData.fmtDate(row.finalBilling) !== "—" ? PRData.fmtDate(row.finalBilling) : null },
      { k: "Comments", v: row.comments },
    ].filter((it) => it.v !== null && it.v !== undefined && it.v !== "")}/>
  );
}

function RawTable({ raw }) {
  if (!raw) return null;
  const entries = Object.entries(raw).filter(([k, v]) => {
    if (v === null || v === undefined || v === "") return false;
    if (typeof v === "string" && !v.trim()) return false;
    return true;
  });
  return (
    <table className="data" style={{fontSize: 11.5}}>
      <tbody>
        {entries.map(([k, v]) => {
          let display = v;
          if (v instanceof Date) display = PRData.fmtDate(v);
          else if (typeof v === "number" && v > 25000 && v < 80000) {
            const d = PRData.toDate(v);
            if (d) display = PRData.fmtDate(d);
          }
          return (
            <tr key={k}>
              <td style={{color: "var(--ink-500)", width: 180, verticalAlign: "top"}}>{k}</td>
              <td style={{color: "var(--ink-900)"}}>{String(display)}</td>
            </tr>
          );
        })}
      </tbody>
    </table>
  );
}

window.SiteDetail = SiteDetail;
