// App shell — wires nav + sections.
const { useEffect: useEffectApp } = React;

const PRODUCTION_ACCENT = {
  hex: "#FF5CAA",
  dim: "rgba(255,92,170,0.12)",
  line: "rgba(255,92,170,0.35)",
};

function pad2(value) {
  return String(value).padStart(2, "0");
}

function getSGDateParts(date) {
  const parts = new Intl.DateTimeFormat("en-CA", {
    timeZone: "Asia/Singapore",
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit",
    second: "2-digit",
    hour12: false,
  }).formatToParts(date);

  return parts.reduce((acc, part) => {
    if (part.type !== "literal") acc[part.type] = part.value;
    return acc;
  }, {});
}

function buildSGIso(parts) {
  return `${parts.year}-${parts.month}-${parts.day}T${parts.hour}:${parts.minute}:${parts.second}+08:00`;
}

function buildMockSGPublicData() {
  const now = new Date();
  const sgToday = getSGDateParts(now);
  const pointCount = 288;
  const intervalMinutes = 5;
  const publicCellCount = 24;
  const targetHour = 14;
  const targetMinute = 5;
  const lastObservedIndex = Math.min(pointCount - 1, targetHour * 12 + Math.floor(targetMinute / intervalMinutes));
  const generatedAtParts = {
    year: sgToday.year,
    month: sgToday.month,
    day: sgToday.day,
    hour: pad2(targetHour),
    minute: pad2(targetMinute),
    second: "32",
  };
  const asOfParts = {
    year: sgToday.year,
    month: sgToday.month,
    day: sgToday.day,
    hour: pad2(targetHour),
    minute: pad2(targetMinute),
    second: "00",
  };

  const dayStartIso = `${sgToday.year}-${sgToday.month}-${sgToday.day}T00:00:00+08:00`;
  const dayEndIso = `${sgToday.year}-${sgToday.month}-${sgToday.day}T23:55:00+08:00`;
  const generatedAt = buildSGIso(generatedAtParts);
  const asOf = buildSGIso(asOfParts);

  const points = [];
  let daylightCaptureIndex = 0;
  let peakIndex = 0;
  let peakTs = asOf;

  for (let index = 0; index < pointCount; index += 1) {
    const hour = (index * intervalMinutes) / 60;
    const pointParts = {
      year: sgToday.year,
      month: sgToday.month,
      day: sgToday.day,
      hour: pad2(Math.floor(hour)),
      minute: pad2((index * intervalMinutes) % 60),
      second: "00",
    };
    const ts = buildSGIso(pointParts);
    let networkOutputIndex = null;
    let activeCellSharePct = null;

    if (index <= lastObservedIndex) {
      const daylightCurve = Math.max(0, Math.sin(((hour - 6.1) / 11.8) * Math.PI));
      const cloudPenalty = Math.max(0, Math.sin(index * 0.17)) * 8 + Math.max(0, Math.cos(index * 0.05)) * 4;
      const lowAmplitudeNoise = Math.sin(index * 0.81) * 1.5 + Math.cos(index * 0.23) * 0.9;
      networkOutputIndex = Math.max(0, Math.min(100, daylightCurve * 88 - cloudPenalty + lowAmplitudeNoise));
      networkOutputIndex = Number(networkOutputIndex.toFixed(1));
      activeCellSharePct = Math.max(32, Math.min(97, 49 + daylightCurve * 43 - cloudPenalty * 0.52));
      activeCellSharePct = Number(activeCellSharePct.toFixed(1));
      daylightCaptureIndex += networkOutputIndex;

      if (networkOutputIndex >= peakIndex) {
        peakIndex = networkOutputIndex;
        peakTs = ts;
      }
    }

    points.push({
      ts,
      network_output_index: networkOutputIndex,
      active_cell_share_pct: activeCellSharePct,
    });
  }

  const currentPoint = points[lastObservedIndex] || points[0];
  const currentIndex = currentPoint && typeof currentPoint.network_output_index === "number" ? currentPoint.network_output_index : 0;
  const currentActiveShare = currentPoint && typeof currentPoint.active_cell_share_pct === "number" ? currentPoint.active_cell_share_pct : 0;
  const baselineDaylightCapture = daylightCaptureIndex / 1.038;

  const featuredCells = [
    { id: "CELL-W-01", cell_label: "W-01", cell_type: "aggregation_cell", status: "ok", map_x: 195, map_y: 440, metrics: { network_output_index: 69.3, active_cell_share_pct: 84.2 } },
    { id: "CELL-W-02", cell_label: "W-02", cell_type: "aggregation_cell", status: "ok", map_x: 361, map_y: 378, metrics: { network_output_index: 72.1, active_cell_share_pct: 86.1 } },
    { id: "CELL-NW-01", cell_label: "NW-01", cell_type: "aggregation_cell", status: "ok", map_x: 376, map_y: 315, metrics: { network_output_index: 70.4, active_cell_share_pct: 83.4 } },
    { id: "CELL-N-01", cell_label: "N-01", cell_type: "aggregation_cell", status: "ok", map_x: 439, map_y: 236, metrics: { network_output_index: 75.2, active_cell_share_pct: 88.3 } },
    { id: "CELL-N-02", cell_label: "N-02", cell_type: "aggregation_cell", status: "watch", map_x: 495, map_y: 220, metrics: { network_output_index: 67.8, active_cell_share_pct: 79.6 } },
    { id: "CELL-N-03", cell_label: "N-03", cell_type: "aggregation_cell", status: "ok", map_x: 534, map_y: 259, metrics: { network_output_index: 73.5, active_cell_share_pct: 87.4 } },
    { id: "CELL-C-01", cell_label: "C-01", cell_type: "aggregation_cell", status: "ok", map_x: 534, map_y: 338, metrics: { network_output_index: 76.4, active_cell_share_pct: 89.1 } },
    { id: "CELL-NE-01", cell_label: "NE-01", cell_type: "aggregation_cell", status: "ok", map_x: 573, map_y: 362, metrics: { network_output_index: 74.1, active_cell_share_pct: 86.8 } },
    { id: "CELL-E-01", cell_label: "E-01", cell_type: "aggregation_cell", status: "ok", map_x: 676, map_y: 322, metrics: { network_output_index: 78.8, active_cell_share_pct: 90.6 } },
    { id: "CELL-E-02", cell_label: "E-02", cell_type: "aggregation_cell", status: "ok", map_x: 754, map_y: 362, metrics: { network_output_index: 80.2, active_cell_share_pct: 91.5 } },
    { id: "CELL-SE-01", cell_label: "SE-01", cell_type: "aggregation_cell", status: "ok", map_x: 649, map_y: 409, metrics: { network_output_index: 76.1, active_cell_share_pct: 88.8 } },
    { id: "CELL-S-01", cell_label: "S-01", cell_type: "aggregation_cell", status: "ok", map_x: 542, map_y: 464, metrics: { network_output_index: 71.3, active_cell_share_pct: 85.1 } },
    { id: "CELL-SW-01", cell_label: "SW-01", cell_type: "aggregation_cell", status: "ok", map_x: 455, map_y: 440, metrics: { network_output_index: 70.6, active_cell_share_pct: 84.9 } },
    { id: "CELL-C-02", cell_label: "C-02", cell_type: "aggregation_cell", status: "ok", map_x: 424, map_y: 420, metrics: { network_output_index: 72.4, active_cell_share_pct: 86.2 } },
    { id: "CELL-C-03", cell_label: "C-03", cell_type: "aggregation_cell", status: "ok", map_x: 471, map_y: 378, metrics: { network_output_index: 74.6, active_cell_share_pct: 87.5 } },
  ];

  const featuredEdges = [
    ["CELL-W-01", "CELL-W-02", 2], ["CELL-W-02", "CELL-C-03", 1], ["CELL-C-03", "CELL-N-01", 1], ["CELL-N-01", "CELL-N-02", 1],
    ["CELL-N-02", "CELL-N-03", 1], ["CELL-N-03", "CELL-C-01", 2], ["CELL-C-01", "CELL-NE-01", 1], ["CELL-NE-01", "CELL-E-01", 1],
    ["CELL-E-01", "CELL-E-02", 2], ["CELL-E-02", "CELL-SE-01", 1], ["CELL-SE-01", "CELL-S-01", 1], ["CELL-S-01", "CELL-SW-01", 1],
    ["CELL-SW-01", "CELL-C-02", 1], ["CELL-C-02", "CELL-C-03", 1], ["CELL-W-02", "CELL-NW-01", 1], ["CELL-NW-01", "CELL-N-01", 1],
    ["CELL-C-01", "CELL-C-03", 2], ["CELL-SE-01", "CELL-NE-01", 1], ["CELL-S-01", "CELL-SW-01", 1], ["CELL-SW-01", "CELL-W-02", 1],
  ].map(([sourceId, targetId, weight]) => ({
    id: `${sourceId}:${targetId}`,
    source_id: sourceId,
    target_id: targetId,
    relation: "telemetry",
    weight,
    status: "ok",
  }));

  return {
    market: "sg",
    summary: {
      market: "sg",
      generated_at: generatedAt,
      as_of: asOf,
      freshness: {
        cadence_seconds: 300,
        lag_seconds: 32,
      },
      window: {
        timezone: "Asia/Singapore",
        interval: "PT5M",
        start: dayStartIso,
        end: dayEndIso,
      },
      coverage: {
        geography_label: "Singapore",
        public_cell_count: publicCellCount,
        cell_scheme: "sg-public-grid-v1",
      },
      aggregate: {
        network_output_index: currentIndex,
        daylight_capture_index: Number((daylightCaptureIndex / Math.max(lastObservedIndex + 1, 1)).toFixed(1)),
        active_cell_share_pct: currentActiveShare,
        variability_index: 18.6,
      },
      comparisons: {
        vs_28d_norm_pct: Number((((daylightCaptureIndex - baselineDaylightCapture) / baselineDaylightCapture) * 100).toFixed(1)),
      },
      peak_today: {
        network_output_index: peakIndex,
        at: peakTs,
      },
      series: {
        metric: "network_output_index",
        unit: "index",
        timezone: "Asia/Singapore",
        interval: "PT5M",
        points,
      },
    },
    network: {
      market: "sg",
      generated_at: generatedAt,
      as_of: asOf,
      viewport: {
        projection: "hero-map-v1",
        width: 1000,
        height: 700,
      },
      coverage: {
        geography_label: "Singapore",
        public_cell_count: publicCellCount,
        cell_scheme: "sg-public-grid-v1",
      },
      aggregate: {
        network_output_index: currentIndex,
        active_cell_share_pct: currentActiveShare,
      },
      topology: {
        featured_node_count: featuredCells.length,
        featured_edge_count: featuredEdges.length,
        nodes: featuredCells,
        edges: featuredEdges,
      },
    },
  };
}

function ensureGraphPublicApi() {
  if (window.GraphPublicApi && window.GraphPublicApi.sg && window.GraphPublicApi.sg.summary && window.GraphPublicApi.sg.network) {
    if (!window.GraphPublicApi.endpoints) {
      window.GraphPublicApi.endpoints = { summary: "/v1/sg/summary", network: "/v1/sg/network" };
    }
    if (!window.GraphPublicApi.getSummary) {
      window.GraphPublicApi.getSummary = function getSummary(region) {
        const key = (region || "sg").toLowerCase();
        return window.GraphPublicApi[key] ? window.GraphPublicApi[key].summary : null;
      };
    }
    if (!window.GraphPublicApi.getNetwork) {
      window.GraphPublicApi.getNetwork = function getNetwork(region) {
        const key = (region || "sg").toLowerCase();
        return window.GraphPublicApi[key] ? window.GraphPublicApi[key].network : null;
      };
    }
    return window.GraphPublicApi;
  }

  const api = window.GraphPublicApi || {};
  api.version = api.version || "concept-c-mock-2026-05";
  api.endpoints = api.endpoints || { summary: "/v1/sg/summary", network: "/v1/sg/network" };
  api.sg = api.sg || buildMockSGPublicData();
  api.getSummary = api.getSummary || function getSummary(region) {
    const key = (region || "sg").toLowerCase();
    return api[key] ? api[key].summary : null;
  };
  api.getNetwork = api.getNetwork || function getNetwork(region) {
    const key = (region || "sg").toLowerCase();
    return api[key] ? api[key].network : null;
  };
  window.GraphPublicApi = api;
  return api;
}

function NavBar({ accent }) {
  return (
    <div className="nav">
      <div className="wrap nav-inner">
        <a className="logo" href="#">
          <svg className="logo-mark" viewBox="0 0 24 24" fill="none">
            <path d="M5 4 L4 4 L4 20 L5 20" stroke="#ECEEF0" strokeWidth="1.1" strokeLinecap="square" />
            <path d="M19 4 L20 4 L20 20 L19 20" stroke="#ECEEF0" strokeWidth="1.1" strokeLinecap="square" />
            <line x1="7" y1="14" x2="11" y2="11" stroke="#ECEEF0" strokeWidth="0.9" />
            <line x1="11" y1="11" x2="14" y2="13" stroke="#ECEEF0" strokeWidth="0.9" />
            <line x1="14" y1="13" x2="17" y2="9" stroke="#ECEEF0" strokeWidth="0.9" />
            <circle cx="14" cy="13" r="1.4" fill={accent} />
          </svg>
          <span className="logo-word">graph</span>
          <span className="mono" style={{ fontSize: 11, color: "var(--ink-3)", letterSpacing: "0.04em", marginLeft: 6, paddingLeft: 10, borderLeft: "1px solid var(--line)" }}>SG</span>
        </a>
        <nav className="nav-links">
          <a href="#products">Products</a>
          <a href="#developers">Developers</a>
          <a href="#dashboard">For asset owners/managers</a>
          <a href="#customers">Customers</a>
          <a href="#about">Company</a>
        </nav>
        <div className="nav-cta">
          <a className="btn btn-ghost" href="#">Sign in</a>
          <a className="btn btn-primary" href="#developers">Get API access</a>
        </div>
      </div>
    </div>
  );
}

function Live({ accent, summaryData, seriesData }) {
  return (
    <section className="sec" id="live" data-screen-label="live">
      <div className="wrap">
        <div className="sec-head">
          <div className="label">04 · LIVE PROOF</div>
          <div>
            <h2>Singapore has a live <em style={{ fontStyle: "normal", color: accent }}>solar network signal</em> right now. We expose it without exposing anyone.</h2>
            <p>This public widget rolls external-page-derived observations into anonymised cells and normalised network indices. No private portfolio totals, no individual site telemetry, refreshed every five minutes.</p>
          </div>
        </div>
        <window.LiveWidget accent={accent} summaryData={summaryData} seriesData={seriesData} />
      </div>
    </section>
  );
}

function App() {
  const accent = PRODUCTION_ACCENT;
  const publicApi = ensureGraphPublicApi();
  const summaryData = publicApi.getSummary("sg");
  const networkData = publicApi.getNetwork("sg");

  useEffectApp(() => {
    document.documentElement.style.setProperty("--accent", accent.hex);
    document.documentElement.style.setProperty("--accent-dim", accent.dim);
    document.documentElement.style.setProperty("--accent-line", accent.line);
  }, [accent.hex]);

  return (
    <>
      <NavBar accent={accent.hex} />
      <window.Hero accent={accent.hex} variant="geo" networkData={networkData} />
      <window.TrustStrip />
      <window.Pillars accent={accent.hex} />
      <window.UseCases accent={accent.hex} />
      <Live accent={accent.hex} summaryData={summaryData} seriesData={summaryData && summaryData.series} />
      <window.Products accent={accent.hex} />
      <window.WhyGraph accent={accent.hex} />
      <window.APIExplorer accent={accent.hex} />
      <window.Dashboard accent={accent.hex} />
      <window.Customers />
      <window.About />
      <window.CTA accent={accent.hex} />
      <window.Footer />
    </>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
