// SQSEO Insights — the deterministic intelligence read on the project's own
// Search Console data (GET /api/insights, engines in server/insights.ts):
// position profile, honest forecast, striking-distance queries, CTR gaps
// against the site's own calibrated curve, decaying pages and keyword
// cannibalization. Every number is real and explainable — no AI, no estimates
// beyond the arithmetic shown. Composes the charts-kit primitives.
(function init(){
if(!window.LongtailIQDesignSystem_ae8f12 || !window.LTQ || !window.LTQ.useApi || !window.LTQ.SectionTitle || !window.LTQ.Sparkline){return setTimeout(init,30);}
const React = window.React;
const DS = window.LongtailIQDesignSystem_ae8f12;
const { Card, Icon, Button } = DS;
const { useApi, fmtCompact, fmtNumber, SectionTitle, EmptyState, Sparkline, StackedBar, Scatter } = window.LTQ;
const t = (k, v) => (window.LTQ.t ? window.LTQ.t(k, v) : k);

const BAND_COLORS = ["var(--viz-green)", "var(--viz-blue)", "var(--viz-violet)", "var(--viz-amber)"];
const Skel = ({ h = 16, w = "100%" }) => <span className="lt-shimmer" style={{ display: "block", height: h, width: w, borderRadius: 6, background: "var(--ink-100)" }} />;
const pathOf = (u) => { try { return decodeURIComponent(new URL(u, "https://x").pathname) || u; } catch (e) { return u; } };
const fmtPct = (v) => window.LTQ.fmtPct1(v);

function Th({ children, right }) {
  return <th style={{ textAlign: right ? "right" : "left", padding: "0 14px", height: 40, background: "var(--ink-50)", borderBottom: "1px solid var(--border-strong)", fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.06em", color: "var(--text-faint)", whiteSpace: "nowrap" }}>{children}</th>;
}
const td = { padding: "10px 14px", borderBottom: "1px solid var(--border-subtle)", fontSize: 13, color: "var(--text-body)" };
const tdNum = { ...td, textAlign: "right", fontFamily: "var(--font-mono)", fontSize: 12.5 };

function CardHead({ title, sub, right }) {
  return (
    <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 10, marginBottom: 14 }}>
      <div>
        <div style={{ fontSize: 14, fontWeight: 600, color: "var(--text-strong)" }}>{title}</div>
        <div style={{ fontSize: 12.5, color: "var(--text-muted)", marginTop: 2, maxWidth: 520 }}>{sub}</div>
      </div>
      {right || null}
    </div>
  );
}

function QuietRow({ text }) {
  return <div style={{ padding: "18px 0 6px", fontSize: 13, color: "var(--text-faint)" }}>{text}</div>;
}

// The deterministic "what stands out" list — the same highlights the weekly
// digest and the shared report show, so all three narrate one story. The server
// sends a key + params rather than a sentence, so it renders in the viewer's
// language here.
function Highlights({ items }) {
  const TONE = { good: "var(--accent-600)", warn: "#b45309", info: "var(--text-muted)" };
  // Numbers get the locale's digit grouping, mirroring the server's
  // renderHighlight — the email and this card must show the same sentence.
  const fmtParams = (p) => {
    const out = {};
    for (const k of Object.keys(p || {})) out[k] = typeof p[k] === "number" ? fmtNumber(p[k]) : p[k];
    return out;
  };
  // The anomaly already has its own banner directly below; repeating it as a
  // bullet would say the same thing twice on one screen.
  const rows = (items || []).filter((h) => h.kind !== "anomaly_spike" && h.kind !== "anomaly_drop");
  if (!rows.length) return null;
  return (
    <Card pad={18}>
      <CardHead title={t("insights.hl.title")} sub={t("insights.hl.sub")} />
      <div style={{ display: "flex", flexDirection: "column", gap: 11 }}>
        {rows.map((h, i) => (
          <div key={i} style={{ display: "flex", gap: 10, alignItems: "flex-start" }}>
            <Icon name={h.icon} size={15} style={{ color: TONE[h.tone] || "var(--text-muted)", marginTop: 2, flexShrink: 0 }} />
            <div style={{ fontSize: 13.5, color: "var(--text-body)", lineHeight: 1.55 }}>{t(h.key, fmtParams(h.params))}</div>
          </div>
        ))}
      </div>
    </Card>
  );
}

// Recent anomaly callout: factual, one line, tone by direction.
function AnomalyBanner({ a }) {
  if (!a) return null;
  const drop = a.dir === "drop";
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 14px", borderRadius: "var(--r-lg)", border: "1px solid var(--border-subtle)", background: drop ? "var(--amber-50)" : "var(--ink-50)" }}>
      <Icon name={drop ? "trending-down" : "trending-up"} size={16} style={{ color: drop ? "var(--viz-amber)" : "var(--viz-green)", flex: "none" }} />
      <span style={{ fontSize: 13, color: "var(--text-body)" }}>
        <b style={{ color: "var(--text-strong)" }}>{t(drop ? "insights.anomaly_drop" : "insights.anomaly_spike")}</b>{" — "}
        {t("insights.anomaly_body", { value: fmtCompact(a.value), expected: fmtCompact(a.expected) })}
      </span>
    </div>
  );
}

// Position profile: one proportional bar over the four rank bands + per-band legend.
function Positions({ buckets }) {
  const total = buckets.reduce((s, b) => s + b.impressions, 0);
  if (!total) return <QuietRow text={t("insights.positions_empty")} />;
  const segments = buckets.map((b, i) => ({ label: b.label, value: b.impressions, color: BAND_COLORS[i] }));
  return (
    <div>
      <StackedBar segments={segments} height={14} legend={false} format={fmtCompact} />
      <div style={{ display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 8, marginTop: 14 }} className="lt-keep-grid">
        {buckets.map((b, i) => (
          <div key={b.label} style={{ borderLeft: "3px solid " + BAND_COLORS[i], paddingLeft: 9 }}>
            <div className="lt-num" style={{ fontFamily: "var(--font-mono)", fontSize: 15, fontWeight: 700, color: "var(--text-strong)" }}>{Math.round(b.share * 100)}%</div>
            <div style={{ fontSize: 11.5, color: "var(--text-muted)" }}>{b.label} · {t("insights.n_queries", { n: b.queries })}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

// Honest projection card: value + 80% band per metric, method note.
function Forecast({ fc }) {
  const method = fc.clicks.method;
  if (method === "flat" || !fc.clicks.points.length) return <QuietRow text={t("insights.method_flat")} />;
  const line = (labelKey, f, color) => {
    const lo = f.points.reduce((s, p) => s + p.lo, 0), hi = f.points.reduce((s, p) => s + p.hi, 0);
    return (
      <div style={{ display: "flex", alignItems: "baseline", gap: 10, flexWrap: "wrap" }}>
        <span style={{ fontSize: 12.5, color: "var(--text-muted)", minWidth: 148 }}>{t(labelKey)}</span>
        <span className="lt-num" style={{ fontFamily: "var(--font-mono)", fontSize: 22, fontWeight: 700, color, letterSpacing: "-0.02em" }}>{fmtCompact(f.total)}</span>
        <span className="lt-num" style={{ fontFamily: "var(--font-mono)", fontSize: 11.5, color: "var(--text-faint)" }}>{t("insights.forecast_range", { lo: fmtCompact(lo), hi: fmtCompact(hi) })}</span>
      </div>
    );
  };
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
      {line("insights.forecast_clicks", fc.clicks, "var(--accent-600)")}
      {line("insights.forecast_impressions", fc.impressions, "var(--text-strong)")}
      <span style={{ display: "inline-flex", alignItems: "center", gap: 6, fontSize: 11.5, color: "var(--text-faint)" }}>
        <Icon name={method === "seasonal" ? "calendar-days" : "trending-up"} size={13} />
        {t(method === "seasonal" ? "insights.method_seasonal" : "insights.method_linear")}
      </span>
    </div>
  );
}

// CTR-gap scatter: flagged queries under the site's own expected-CTR curve.
function GapScatter({ gaps, curve }) {
  if (gaps.length < 3) return null;
  const pts = gaps.map((g) => ({ x: g.position, y: g.ctr, label: g.query, color: "var(--viz-amber)" }));
  // Start the x axis just left of the shallowest flagged position so the curve's
  // steep head (position-1 CTR) doesn't crush the points against the floor.
  const x0 = Math.max(1, Math.min(15, Math.floor(Math.min(...pts.map((p) => p.x))) - 1));
  const expAt = (p) => {
    const c = curve.curve, cl = Math.max(1, Math.min(20, p));
    const lo = Math.floor(cl), hi = Math.ceil(cl), f = cl - lo;
    return (c[lo - 1] * (1 - f) + c[Math.min(19, hi - 1)] * f) * 100;
  };
  const yMax = Math.max(expAt(x0), ...pts.map((p) => p.y)) * 1.15;
  return (
    <div style={{ marginBottom: 6 }}>
      <Scatter points={pts} height={190} xDomain={[x0, 20]} yDomain={[0, yMax]}
        xLabel={t("insights.scatter_x")} yLabel={t("insights.scatter_y")}
        refLine={expAt} refLabel={t("insights.scatter_ref")}
        format={(v) => window.LTQ.fmtDec1(v)} xFormat={(v) => String(Math.round(v))} />
    </div>
  );
}

function Insights({ onNavigate }) {
  const { loading, data } = useApi("/api/insights");
  const b = data && data.insights;
  const empty = !loading && (!b || (b.counts.queries === 0 && b.counts.pages === 0));
  const curveNote = b && b.ctrCurve ? t("insights.curve_" + b.ctrCurve.source) : "";

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 18 }}>
      <SectionTitle eyebrow={t("insights.eyebrow")} title={t("nav.insights")} sub={t("insights.sub")} />

      {loading ? (
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14 }} className="lt-ov-grid">
          {[0, 1, 2, 3].map((i) => <Card key={i} pad={18}><Skel h={120} /></Card>)}
        </div>
      ) : empty ? (
        <Card pad={0}><EmptyState icon="activity" title={t("insights.empty_title")} body={t("insights.empty_body")}
          action={<Button variant="primary" leadingIcon="line-chart" onClick={() => onNavigate && onNavigate("performance")}>{t("insights.open_performance")}</Button>} /></Card>
      ) : b ? (
        <>
          <Highlights items={data && data.highlights} />
          <AnomalyBanner a={b.anomalies && b.anomalies.recent} />

          {/* position profile + forecast */}
          <div style={{ display: "grid", gridTemplateColumns: "1.3fr 1fr", gap: 14, alignItems: "stretch" }} className="lt-ov-grid">
            <Card pad={18}>
              <CardHead title={t("insights.positions_title")} sub={t("insights.positions_sub")} />
              <Positions buckets={b.positions} />
            </Card>
            <Card pad={18}>
              <CardHead title={t("insights.forecast_title", { n: b.forecast.clicks.points.length || 0 })} sub={t("insights.forecast_sub")} />
              <Forecast fc={b.forecast} />
            </Card>
          </div>

          {/* striking distance */}
          <Card pad={0}>
            <div style={{ padding: "18px 18px 0" }}>
              <CardHead title={t("insights.striking_title")} sub={t("insights.striking_sub")} />
            </div>
            {b.striking.length === 0 ? (
              <div style={{ padding: "0 18px 18px" }}><QuietRow text={t("insights.striking_empty")} /></div>
            ) : (
              <div style={{ overflowX: "auto" }} className="lt-scroll">
                <table style={{ borderCollapse: "collapse", width: "100%" }}>
                  <thead><tr><Th>{t("insights.col_query")}</Th><Th right>{t("insights.col_position")}</Th><Th right>{t("insights.col_impressions")}</Th><Th right>{t("insights.col_ctr")}</Th><Th right>{t("insights.col_potential")}</Th></tr></thead>
                  <tbody>
                    {b.striking.slice(0, 8).map((s, i) => (
                      <tr key={i}>
                        <td style={{ ...td, fontWeight: 600, color: "var(--text-strong)" }}>{s.query}
                          {s.url && <span style={{ display: "block", fontSize: 11.5, fontWeight: 400, color: "var(--text-faint)", marginTop: 1 }}>{pathOf(s.url)}</span>}</td>
                        <td style={tdNum}>{s.position}</td>
                        <td style={tdNum}>{fmtCompact(s.impressions)}</td>
                        <td style={tdNum}>{fmtPct(s.ctr)}</td>
                        <td style={{ ...tdNum, fontWeight: 700, color: s.potential > 0 ? "var(--viz-green)" : "var(--text-faint)" }}>{s.potential > 0 ? "+" + fmtCompact(s.potential) : "0"}</td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            )}
          </Card>

          {/* ctr gaps + decaying pages */}
          <div style={{ display: "grid", gridTemplateColumns: "1.3fr 1fr", gap: 14, alignItems: "stretch" }} className="lt-ov-grid">
            <Card pad={18}>
              <CardHead title={t("insights.gaps_title")} sub={t("insights.gaps_sub")}
                right={<span style={{ fontSize: 11, color: "var(--text-faint)", whiteSpace: "nowrap", paddingTop: 3 }}>{curveNote}</span>} />
              {b.ctrGaps.length === 0 ? <QuietRow text={t("insights.gaps_empty")} /> : (
                <>
                  <GapScatter gaps={b.ctrGaps} curve={b.ctrCurve} />
                  <div style={{ display: "flex", flexDirection: "column" }}>
                    {b.ctrGaps.slice(0, 6).map((g, i) => (
                      <div key={i} style={{ display: "flex", alignItems: "center", gap: 10, padding: "9px 0", borderBottom: i < Math.min(6, b.ctrGaps.length) - 1 ? "1px solid var(--border-subtle)" : "none" }}>
                        <div style={{ flex: 1, minWidth: 0 }}>
                          <div style={{ fontSize: 13, fontWeight: 600, color: "var(--text-strong)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{g.query}</div>
                          <div className="lt-num" style={{ fontSize: 11.5, fontFamily: "var(--font-mono)", color: "var(--text-faint)" }}>#{g.position} · {fmtPct(g.ctr)} → {fmtPct(g.expected)}</div>
                        </div>
                        <span className="lt-num" style={{ fontFamily: "var(--font-mono)", fontSize: 12.5, fontWeight: 700, color: "var(--viz-amber)", flex: "none" }}>−{fmtCompact(g.lostClicks)}</span>
                      </div>
                    ))}
                  </div>
                </>
              )}
            </Card>

            <Card pad={18}>
              <CardHead title={t("insights.decay_title")} sub={t("insights.decay_sub")} />
              {b.decaying.length === 0 ? <QuietRow text={t("insights.decay_empty")} /> : (
                <div style={{ display: "flex", flexDirection: "column" }}>
                  {b.decaying.slice(0, 6).map((p, i) => (
                    <div key={i} style={{ display: "flex", alignItems: "center", gap: 12, padding: "9px 0", borderBottom: i < Math.min(6, b.decaying.length) - 1 ? "1px solid var(--border-subtle)" : "none" }}>
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ fontSize: 13, fontWeight: 600, color: "var(--text-strong)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{p.title || pathOf(p.url || "")}</div>
                        <div className="lt-num" style={{ fontSize: 11.5, fontFamily: "var(--font-mono)", color: "var(--viz-red)" }}>{p.momentum.changePct}% · {t("insights.n_clicks", { n: fmtCompact(p.clicks) })}</div>
                      </div>
                      <Sparkline points={p.trend || []} color="var(--viz-red)" width={92} height={26} />
                    </div>
                  ))}
                </div>
              )}
            </Card>
          </div>

          {/* cannibalization */}
          <Card pad={18}>
            <CardHead title={t("insights.cann_title")} sub={t("insights.cann_sub")} />
            {b.cannibalization.length === 0 ? <QuietRow text={t("insights.cann_empty")} /> : (
              <div style={{ display: "flex", flexDirection: "column" }}>
                {b.cannibalization.slice(0, 5).map((cn, i) => (
                  <div key={i} style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 0", borderBottom: i < Math.min(5, b.cannibalization.length) - 1 ? "1px solid var(--border-subtle)" : "none", flexWrap: "wrap" }}>
                    <div style={{ flex: "1 1 260px", minWidth: 0 }}>
                      <div style={{ fontSize: 13, fontWeight: 600, color: "var(--text-strong)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{cn.sampleQuery}</div>
                      <div style={{ fontSize: 11.5, color: "var(--text-faint)", marginTop: 1 }}>{t("insights.cann_pages", { n: cn.pages.length })} · {fmtCompact(cn.totalImpressions)} {t("dash.metric.impressions").toLowerCase()}</div>
                    </div>
                    <span style={{ display: "inline-flex", alignItems: "center", gap: 6, fontSize: 12, color: "var(--text-body)", flex: "none" }}>
                      <Icon name="crown" size={13} style={{ color: "var(--viz-green)" }} />
                      <span className="lt-num" style={{ fontFamily: "var(--font-mono)", fontSize: 12 }}>{pathOf(cn.keeper)}</span>
                    </span>
                    <span className="lt-num" style={{ fontFamily: "var(--font-mono)", fontSize: 12.5, fontWeight: 700, color: "var(--viz-amber)", flex: "none" }}>{t("insights.cann_split", { n: fmtCompact(cn.splitClicks) })}</span>
                  </div>
                ))}
              </div>
            )}
          </Card>
        </>
      ) : null}
    </div>
  );
}

window.LTQ = window.LTQ || {};
window.LTQ.Insights = Insights;
})();
