(() => {
  const { useMemo, useState } = React;

  function asArray(value) {
    return Array.isArray(value) ? value : [];
  }

  function toNumber(value) {
    const parsed = Number(value);
    return Number.isFinite(parsed) ? parsed : 0;
  }

  function asUtcDate(value) {
    const text = String(value || "").trim();
    if (!text) {
      return null;
    }

    const normalized = text.includes("T") ? text : text.replace(" ", "T");
    const date = new Date(`${normalized}Z`);
    return Number.isNaN(date.getTime()) ? null : date;
  }

  function formatDateTime(value, fallback = "Unavailable") {
    const date = asUtcDate(value);
    if (!date) {
      return fallback;
    }

    return date.toLocaleString("en-GB", {
      day: "2-digit",
      month: "short",
      year: "numeric",
      hour: "2-digit",
      minute: "2-digit",
      timeZone: "UTC"
    }) + " UTC";
  }

  function formatDate(value, fallback = "Unavailable") {
    const date = asUtcDate(value);
    if (!date) {
      return fallback;
    }

    return date.toLocaleDateString("en-GB", {
      day: "2-digit",
      month: "short",
      year: "numeric",
      timeZone: "UTC"
    });
  }

  function formatMoney(value, currency = "USD", signed = false) {
    const amount = toNumber(value);
    const prefix = signed && amount > 0 ? "+" : signed && amount < 0 ? "-" : "";
    return `${String(currency || "USD").toUpperCase()} ${prefix}${Math.abs(amount).toLocaleString(undefined, {
      minimumFractionDigits: 2,
      maximumFractionDigits: 2
    })}`;
  }

  function formatNumber(value, options = {}) {
    return toNumber(value).toLocaleString(undefined, options);
  }

  function formatPips(value, signed = false) {
    const amount = Number(value);
    if (!Number.isFinite(amount)) {
      return "Not matched";
    }

    const prefix = signed && amount > 0 ? "+" : signed && amount < 0 ? "-" : "";
    return `${prefix}${Math.abs(amount).toLocaleString(undefined, {
      minimumFractionDigits: 1,
      maximumFractionDigits: 1
    })} pips`;
  }

  function badgeClassForNumber(value) {
    const amount = toNumber(value);
    if (amount < 0) {
      return "badge-danger";
    }
    if (amount === 0) {
      return "badge-gold";
    }
    return "";
  }

  function accountKeyFromItem(item) {
    const login = String(item && item.account_login ? item.account_login : "").trim() || "unknown";
    const server = String(item && item.server_name ? item.server_name : "").trim() || "unknown";
    return `${login}|${server}`;
  }

  function computeSummary(items) {
    const summary = {
      total_trades: asArray(items).length,
      closed_trades: 0,
      winning_trades: 0,
      losing_trades: 0,
      profit: 0,
      loss: 0,
      pips_won: 0,
      pips_lost: 0,
      net_total: 0
    };

    asArray(items).forEach((item) => {
      const net = toNumber(item.net_result);
      const pips = Number(item.pips);
      const entry = String(item.deal_entry || "").toLowerCase();

      summary.net_total += net;

      if (net > 0) {
        summary.profit += net;
        summary.winning_trades += 1;
      } else if (net < 0) {
        summary.loss += Math.abs(net);
        summary.losing_trades += 1;
      }

      if (["out", "out_by", "inout"].includes(entry)) {
        summary.closed_trades += 1;
      }

      if (Number.isFinite(pips)) {
        if (pips > 0) {
          summary.pips_won += pips;
        } else if (pips < 0) {
          summary.pips_lost += Math.abs(pips);
        }
      }
    });

    return summary;
  }

  function computeRunningChartPoints(items) {
    let running = 0;

    return asArray(items)
      .slice()
      .sort((left, right) => {
        const leftTime = asUtcDate(left.deal_time);
        const rightTime = asUtcDate(right.deal_time);
        const leftValue = leftTime ? leftTime.getTime() : 0;
        const rightValue = rightTime ? rightTime.getTime() : 0;
        if (leftValue === rightValue) {
          return toNumber(left.id) - toNumber(right.id);
        }
        return leftValue - rightValue;
      })
      .map((item) => {
        running += toNumber(item.net_result);
        return {
          label: formatDateTime(item.deal_time, "Recent trade"),
          symbol: String(item.symbol || "").trim(),
          value: Number(running.toFixed(2))
        };
      });
  }

  function buildChartGeometry(points, width = 860, height = 250) {
    const list = asArray(points);
    if (!list.length) {
      return null;
    }

    const padX = 28;
    const padTop = 18;
    const padBottom = 30;
    const values = list.map((point) => toNumber(point.value));
    let minValue = Math.min(...values);
    let maxValue = Math.max(...values);

    if (Math.abs(maxValue - minValue) < 0.01) {
      minValue -= 1;
      maxValue += 1;
    }

    const plotWidth = width - padX * 2;
    const plotHeight = height - padTop - padBottom;
    const range = maxValue - minValue || 1;
    const coords = list.map((point, index) => {
      const x = padX + plotWidth * (index / Math.max(list.length - 1, 1));
      const y = padTop + ((maxValue - toNumber(point.value)) / range) * plotHeight;
      return `${x.toFixed(2)},${y.toFixed(2)}`;
    });
    const zeroLineY = minValue <= 0 && maxValue >= 0
      ? padTop + ((maxValue - 0) / range) * plotHeight
      : null;

    return {
      width,
      height,
      padX,
      polyline: coords.join(" "),
      area: `${padX},${height - padBottom} ${coords.join(" ")} ${width - padX},${height - padBottom}`,
      maxLabel: formatMoney(maxValue),
      minLabel: formatMoney(minValue),
      startLabel: String(list[0].label || "Recent history"),
      endLabel: String(list[list.length - 1].label || "Latest result"),
      zeroLineY: zeroLineY == null ? null : zeroLineY.toFixed(2)
    };
  }

  function NavLink({ item, active }) {
    return (
      <a className={`ms-react-nav-link${active ? " active" : ""}`} href={item.href}>
        <i className={`bi ${item.icon}`}></i>
        <span>{item.label}</span>
      </a>
    );
  }

  function PortalShell({ data }) {
    const [menuOpen, setMenuOpen] = useState(false);
    const navItems = asArray(data.navItems);

    return (
      <section className="ms-react-shell-card">
        <div className="ms-react-shell-meta">
          <div>
            <div className="eyebrow">Connected workspace</div>
            <div className="ms-react-shell-title">{data.pageTitle || "Portal"}</div>
            <p className="muted mb-0">
              Switch across the full portal from the top menu and keep mobile navigation open when you need it.
            </p>
          </div>
          <div className="ms-react-shell-actions">
            <span className="mini-badge">{data.roleLabel || "Portal"}</span>
            <span className="quick-pill">{data.userName || "User"}</span>
            <button className="btn-ghost btn-inline ms-react-menu-toggle" type="button" onClick={() => setMenuOpen(!menuOpen)}>
              <i className={`bi ${menuOpen ? "bi-x-lg" : "bi-list"}`}></i>
              <span>{menuOpen ? "Close" : "Menu"}</span>
            </button>
          </div>
        </div>
        <div className={`ms-react-nav-strip${menuOpen ? " is-open" : ""}`}>
          {navItems.map((item) => (
            <NavLink key={item.key} item={item} active={item.key === data.activeKey} />
          ))}
        </div>
      </section>
    );
  }

  function PublicShell({ data }) {
    const [menuOpen, setMenuOpen] = useState(false);
    const links = asArray(data.links);

    return (
      <section className="ms-react-shell-card public-react-shell">
        <div className="ms-react-shell-meta">
          <div>
            <div className="eyebrow">Top menu</div>
            <div className="ms-react-shell-title">Explore the full demo flow</div>
            <p className="muted mb-0">
              Jump between pricing, partners, training, and the 7-day demo registration area from one responsive menu.
            </p>
          </div>
          <button className="btn-ghost btn-inline ms-react-menu-toggle" type="button" onClick={() => setMenuOpen(!menuOpen)}>
            <i className={`bi ${menuOpen ? "bi-x-lg" : "bi-list"}`}></i>
            <span>{menuOpen ? "Close" : "Menu"}</span>
          </button>
        </div>
        <div className={`ms-react-nav-strip${menuOpen ? " is-open" : ""}`}>
          {links.map((item) => (
            <a key={item.key} className={`ms-react-nav-link${item.key === data.activeKey ? " active" : ""}`} href={item.href}>
              <span>{item.label}</span>
            </a>
          ))}
        </div>
      </section>
    );
  }

  function StatCard({ label, value, copy }) {
    return (
      <article className="report-item">
        <div className="report-label">{label}</div>
        <div className="report-value">{value}</div>
        {copy ? <div className="report-copy">{copy}</div> : null}
      </article>
    );
  }

  function JournalAccountPill({ group, active, onClick }) {
    return (
      <button className={`ms-account-pill${active ? " active" : ""}`} type="button" onClick={onClick}>
        <span className="ms-account-pill-login">{group.account_login || "Unknown account"}</span>
        <span className="ms-account-pill-meta">
          {group.server_name || "No server"} - {formatMoney(group.net_total, "USD", true)}
        </span>
      </button>
    );
  }

  function JournalChart({ points }) {
    const chart = buildChartGeometry(points);

    if (!chart) {
      return (
        <div className="empty-state">No chart movement is available for this account yet.</div>
      );
    }

    return (
      <div className="journal-chart-wrap mt-3">
        <div className="journal-chart-meta">
          <span>{chart.maxLabel}</span>
          <span>{chart.startLabel}</span>
        </div>
        <svg className="journal-chart" viewBox={`0 0 ${chart.width} ${chart.height}`} role="img" aria-label="Account movement chart">
          <defs>
            <linearGradient id="journalChartFillReact" x1="0" x2="0" y1="0" y2="1">
              <stop offset="0%" stopColor="#178c49" stopOpacity="0.28"></stop>
              <stop offset="100%" stopColor="#178c49" stopOpacity="0.02"></stop>
            </linearGradient>
          </defs>
          {chart.zeroLineY ? (
            <line
              x1={chart.padX}
              y1={chart.zeroLineY}
              x2={chart.width - chart.padX}
              y2={chart.zeroLineY}
              className="journal-chart-zero"
            ></line>
          ) : null}
          <polygon points={chart.area} fill="url(#journalChartFillReact)"></polygon>
          <polyline points={chart.polyline} className="journal-chart-line"></polyline>
        </svg>
        <div className="journal-chart-meta">
          <span>{chart.minLabel}</span>
          <span>{chart.endLabel}</span>
        </div>
      </div>
    );
  }

  function JournalTradeCard({ item, role }) {
    const net = toNumber(item.net_result);
    const charges = toNumber(item.swap) + toNumber(item.commission) + toNumber(item.fee);
    const typeClass = String(item.deal_type || "").toLowerCase() === "sell"
      ? "badge-danger"
      : String(item.deal_type || "").toLowerCase() === "buy"
        ? ""
        : "badge-gold";
    const entry = String(item.deal_entry || "").toLowerCase();
    const entryLabel = {
      in: "Open",
      out: "Close",
      inout: "Reverse",
      out_by: "Close By",
      other: "Other"
    }[entry] || "Other";

    return (
      <article className="item-card journal-trade-card">
        <div className="item-row">
          <div>
            <div className="item-title">{item.symbol || "Trade row"}</div>
            <div className="item-sub">
              {formatDateTime(item.deal_time, "No deal time")} - Ticket #{item.deal_ticket || "0"}
            </div>
          </div>
          <span className={`mini-badge ${badgeClassForNumber(net)}`}>{formatMoney(net, "USD", true)}</span>
        </div>
        <div className="meta-line mt-3">
          <span className={`mini-badge ${typeClass}`}>{String(item.deal_type || "other").toUpperCase()}</span>
          <span className={`mini-badge ${entry === "out" || entry === "out_by" ? "badge-gold" : ""}`}>{entryLabel}</span>
          <span className={`mini-badge ${badgeClassForNumber(item.pips)}`}>{formatPips(item.pips, true)}</span>
        </div>
        <div className="kv">
          {role !== "client" ? (
            <div className="kv-box">
              <div className="kv-label">Client</div>
              <div className="kv-value">{item.client_name || "Unassigned Client"}</div>
            </div>
          ) : null}
          <div className="kv-box">
            <div className="kv-label">Account</div>
            <div className="kv-value">{item.account_login || "No login"} / {item.server_name || "No server"}</div>
          </div>
          <div className="kv-box">
            <div className="kv-label">License</div>
            <div className="kv-value">{item.license_key || "Unlinked"}</div>
          </div>
          <div className="kv-box">
            <div className="kv-label">Volume / Price</div>
            <div className="kv-value">{formatNumber(item.volume, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} / {formatNumber(item.price, { minimumFractionDigits: 2, maximumFractionDigits: 5 })}</div>
          </div>
          <div className="kv-box">
            <div className="kv-label">Raw Profit</div>
            <div className="kv-value">{formatMoney(item.profit, "USD", true)}</div>
          </div>
          <div className="kv-box">
            <div className="kv-label">Charges</div>
            <div className="kv-value">{formatMoney(charges, "USD", true)}</div>
          </div>
        </div>
        <div className="ms-react-note">
          {item.comment ? item.comment : "No comment saved for this transaction."}
        </div>
      </article>
    );
  }

  function JournalApp({ data }) {
    const items = asArray(data.items);
    const accountGroups = asArray(data.account_groups);
    const defaultAccountKey = data.selected_account_key || "";
    const [selectedAccountKey, setSelectedAccountKey] = useState(defaultAccountKey);

    const filteredItems = useMemo(() => {
      if (!selectedAccountKey) {
        return items;
      }
      return items.filter((item) => accountKeyFromItem(item) === selectedAccountKey);
    }, [items, selectedAccountKey]);

    const activeGroup = useMemo(() => {
      return accountGroups.find((group) => group.account_key === selectedAccountKey) || null;
    }, [accountGroups, selectedAccountKey]);

    const summary = useMemo(() => {
      return selectedAccountKey ? computeSummary(filteredItems) : (data.summary || computeSummary(filteredItems));
    }, [data.summary, filteredItems, selectedAccountKey]);

    const chartPoints = useMemo(() => {
      return selectedAccountKey ? computeRunningChartPoints(filteredItems) : asArray(data.chart_points);
    }, [data.chart_points, filteredItems, selectedAccountKey]);

    if (!items.length && !accountGroups.length) {
      return <section className="empty-state">No transaction journal activity has been recorded for this account yet.</section>;
    }

    return (
      <div className="section-stack">
        <section className="ms-card">
          <div className="item-row">
            <div>
              <div className="section-title">Account-Based Journal Switcher</div>
              <div className="muted">
                Select a trading account to focus the journal on that account&apos;s movement, or leave it on all accounts for the global view.
              </div>
            </div>
            <span className="quick-pill">{accountGroups.length} tracked account{accountGroups.length === 1 ? "" : "s"}</span>
          </div>
          <div className="ms-account-pill-row">
            <button className={`ms-account-pill${selectedAccountKey === "" ? " active" : ""}`} type="button" onClick={() => setSelectedAccountKey("")}>
              <span className="ms-account-pill-login">All accounts</span>
              <span className="ms-account-pill-meta">Combined movement and latest journal entries</span>
            </button>
            {accountGroups.map((group) => (
              <JournalAccountPill
                key={group.account_key}
                group={group}
                active={group.account_key === selectedAccountKey}
                onClick={() => setSelectedAccountKey(group.account_key)}
              />
            ))}
          </div>
          {activeGroup ? (
            <div className="kv mt-3">
              <div className="kv-box">
                <div className="kv-label">Account Name</div>
                <div className="kv-value">{activeGroup.account_name || "Not saved"}</div>
              </div>
              <div className="kv-box">
                <div className="kv-label">Broker / Server</div>
                <div className="kv-value">{activeGroup.broker || "No broker"} / {activeGroup.server_name || "No server"}</div>
              </div>
              <div className="kv-box">
                <div className="kv-label">Latest Trade</div>
                <div className="kv-value">{formatDateTime(activeGroup.latest_trade_at, "No trade yet")}</div>
              </div>
              <div className="kv-box">
                <div className="kv-label">Tracked Symbols</div>
                <div className="kv-value">{formatNumber(activeGroup.symbols_count || 0)}</div>
              </div>
            </div>
          ) : null}
        </section>

        <section className="report-grid journal-summary-grid">
          <StatCard label="Total Trades" value={formatNumber(summary.total_trades || 0)} copy={`${formatNumber(summary.closed_trades || 0)} closing transactions recorded.`} />
          <StatCard label="Profit" value={formatMoney(summary.profit || 0)} copy={`${formatNumber(summary.winning_trades || 0)} winning rows contributed.`} />
          <StatCard label="Loss" value={formatMoney(summary.loss || 0)} copy={`${formatNumber(summary.losing_trades || 0)} losing rows contributed.`} />
          <StatCard label="Pips Won" value={formatPips(summary.pips_won || 0)} copy="Positive pip movement from matched closes." />
          <StatCard label="Net Result" value={formatMoney(summary.net_total || 0, "USD", true)} copy={selectedAccountKey ? "Focused on the selected trading account." : "Combined across all visible accounts."} />
        </section>

        <section className="ms-card">
          <div className="item-row">
            <div>
              <div className="section-title">Cumulative Account Movement</div>
              <div className="muted">
                This curve tracks the running net result using profit, swap, commission, and fee values from the selected journal data.
              </div>
            </div>
            <span className="quick-pill">{selectedAccountKey ? "Focused account" : "All visible accounts"}</span>
          </div>
          <JournalChart points={chartPoints} />
        </section>

        <section className="ms-card">
          <div className="item-row">
            <div>
              <div className="section-title">Recent Transaction Movement</div>
              <div className="muted">
                The most recent journal transactions stay in sync with the selected account so you can inspect movement quickly on both desktop and mobile.
              </div>
            </div>
            <span className="quick-pill">{filteredItems.length} visible rows</span>
          </div>
          {filteredItems.length ? (
            <div className="card-list mt-3">
              {filteredItems.map((item) => (
                <JournalTradeCard key={`${item.id}-${item.deal_ticket || "trade"}`} item={item} role={data.role || "client"} />
              ))}
            </div>
          ) : (
            <div className="empty-state mt-3">No recent journal rows match the selected account yet.</div>
          )}
        </section>
      </div>
    );
  }

  function ReferralOverview({ data }) {
    const role = String(data.role || "client");
    const managerLinks = asArray(data.active_links);
    const partnerClients = asArray(data.linked_clients);
    const topPartners = asArray(data.partner_report);
    const sourceRows = role === "manager" ? managerLinks : partnerClients;

    const totals = sourceRows.reduce((accumulator, row) => {
      accumulator.tradeRows += toNumber(row.movement_trade_count);
      accumulator.netMovement += toNumber(row.movement_net_total);
      accumulator.liveAccounts += toNumber(row.movement_accounts_count);
      const currentTrade = asUtcDate(row.latest_trade_at);
      if (currentTrade && (!accumulator.latestTrade || currentTrade > accumulator.latestTrade)) {
        accumulator.latestTrade = currentTrade;
      }
      return accumulator;
    }, {
      tradeRows: 0,
      netMovement: 0,
      liveAccounts: 0,
      latestTrade: null
    });

    const spotlightRows = role === "manager" ? topPartners : sourceRows;

    if (role === "client") {
      return (
        <section className="ms-card">
          <div className="item-row">
            <div>
              <div className="section-title">Referral Progress Snapshot</div>
              <div className="muted">
                Your referral area now stays connected to the wider portal so you can move from request status into accounts, payments, and journal activity more easily.
              </div>
            </div>
            <span className="quick-pill">Client view</span>
          </div>
          <div className="report-grid mt-3">
            <StatCard label="Trade Journal Rows" value={formatNumber(data.metrics && data.metrics.journals_recent)} copy="Recent activity from your visible journal scope." />
            <StatCard label="Active Licenses" value={formatNumber(data.metrics && data.metrics.active_licenses)} copy="Visible from the same connected workspace." />
            <StatCard label="Paid Revenue" value={formatMoney(data.metrics && data.metrics.payments_total)} copy="Your recorded paid payments so far." />
          </div>
        </section>
      );
    }

    return (
      <section className="ms-card">
        <div className="item-row">
          <div>
            <div className="section-title">Live Referral Movement</div>
            <div className="muted">
              Referral reporting now includes journal movement totals, latest trade timing, and live account activity from the linked trading flow.
            </div>
          </div>
          <span className="quick-pill">{role === "manager" ? "Manager overview" : "Partner overview"}</span>
        </div>
        <div className="report-grid mt-3">
          <StatCard label="Movement Rows" value={formatNumber(totals.tradeRows)} copy="Journal transaction rows from linked clients." />
          <StatCard label="Net Movement" value={formatMoney(totals.netMovement, "USD", true)} copy="Combined net movement from linked journal activity." />
          <StatCard label="Live Accounts" value={formatNumber(totals.liveAccounts)} copy="Distinct trading accounts visible in linked movement." />
          <StatCard label="Latest Trade" value={totals.latestTrade ? formatDateTime(totals.latestTrade.toISOString().replace("T", " ").slice(0, 19)) : "No journal yet"} copy="Most recent linked journal time." />
        </div>
        {spotlightRows.length ? (
          <div className="card-list mt-3">
            {spotlightRows.slice(0, 4).map((row, index) => (
              <article className="item-card" key={`${row.partner_portal_account_id || row.user_id || index}`}>
                <div className="item-row">
                  <div>
                    <div className="item-title">{row.partner_name || row.full_name || row.client_name || "Linked account"}</div>
                    <div className="item-sub">{row.partner_email || row.email || row.client_email || "Connected portal record"}</div>
                  </div>
                  <span className={`mini-badge ${badgeClassForNumber(row.movement_net_total)}`}>{formatMoney(row.movement_net_total, "USD", true)}</span>
                </div>
                <div className="kv mt-3">
                  <div className="kv-box">
                    <div className="kv-label">Movement Rows</div>
                    <div className="kv-value">{formatNumber(row.movement_trade_count || 0)}</div>
                  </div>
                  <div className="kv-box">
                    <div className="kv-label">Live Accounts</div>
                    <div className="kv-value">{formatNumber(row.movement_accounts_count || 0)}</div>
                  </div>
                  <div className="kv-box">
                    <div className="kv-label">Latest Trade</div>
                    <div className="kv-value">{formatDateTime(row.latest_trade_at, "No journal yet")}</div>
                  </div>
                  <div className="kv-box">
                    <div className="kv-label">{role === "manager" ? "Paid Revenue" : "Pending Commission"}</div>
                    <div className="kv-value">{role === "manager" ? formatMoney(row.paid_revenue || 0) : formatMoney(row.pending_commission || 0)}</div>
                  </div>
                </div>
              </article>
            ))}
          </div>
        ) : null}
      </section>
    );
  }

  function mount(node, element) {
    if (!node) {
      return;
    }

    ReactDOM.createRoot(node).render(element);
  }

  mount(document.getElementById("ms-react-portal-shell"), <PortalShell data={window.MarketShiftPortalShell || {}} />);
  mount(document.getElementById("ms-react-public-shell"), <PublicShell data={window.MarketShiftPublicShell || {}} />);
  mount(document.getElementById("ms-react-journal-app"), <JournalApp data={window.MarketShiftJournalData || {}} />);
  mount(document.getElementById("ms-react-referral-app"), <ReferralOverview data={window.MarketShiftReferralData || {}} />);
})();
