/* app.jsx — shell: brand header + tabbed decoder / chain builder. */
const appDS = window.DeltaBlueDesignSystem_b0e437;
const { Tabs } = appDS;

function Wordmark() {
  return <img src="assets/deltablue-logo.svg" alt="DeltaBlue" style={{ height: 28, width: 'auto', display: 'block' }} />;
}

function Header() {
  return (
    <header style={{ borderBottom: '1px solid var(--border)', background: 'var(--surface-card)', position: 'sticky', top: 0, zIndex: 20 }}>
      <div style={{ maxWidth: 'var(--container-max)', margin: '0 auto', padding: '0 var(--space-8)' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: 'var(--space-5) 0', gap: 'var(--space-4)', flexWrap: 'wrap' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-4)' }}>
            <Wordmark />
            <div style={{ height: 22, width: 1, background: 'var(--border-strong)' }} />
            <div>
              <div style={{ font: 'var(--body)', fontWeight: 600, color: 'var(--text-strong)', lineHeight: 1.1 }}>SSL Certificate Toolkit</div>
              <div style={{ font: 'var(--caption)', textTransform: 'uppercase', letterSpacing: 'var(--tracking-eyebrow)', color: 'var(--text-brand)', marginTop: 2 }}>Decode · Verify · Complete chains</div>
            </div>
          </div>
        </div>
      </div>
    </header>
  );
}

// Fold an "optimality" verdict into the structural health report: if the chain
// the user supplied is valid but isn't the most-compatible option discovered,
// downgrade to a warning and point at the recommended one.
function withOptimality(health, chainResult) {
  if (!health || !chainResult || !chainResult.options) return health;
  const current = chainResult.options.find((o) => o.current);
  const recommended = chainResult.options.find((o) => o.recommended);
  if (!current || !recommended || current === recommended) return health;

  let text;
  if (recommended.terminus.label === current.terminus.label) {
    // same trust anchor — the recommended option is a better issuance of the
    // same chain (typically a re-issued intermediate that's valid longer)
    text = 'A preferable chain is recommended below — it reaches the same root (' +
      recommended.terminus.label + ')';
    if (recommended.caExpiry && current.caExpiry && recommended.caExpiry > current.caExpiry) {
      text += ' but its intermediate is valid longer (until ' + fmtDate(recommended.caExpiry) +
        ', vs ' + fmtDate(current.caExpiry) + ')';
    }
    text += '.';
  } else {
    text = 'A more compatible chain is available — the recommended option cross-signs up to ' +
      recommended.terminus.label + ', while the chain provided reaches ' + current.terminus.label +
      '. See the “Most compatible” option on the Certificate chain tab.';
  }

  const items = health.items.concat([{ level: 'warning', text }]);
  const level = health.level === 'error' ? 'error' : 'warning';
  const headline = level === 'error' ? health.headline : 'This chain works, but can be improved';
  return { level, headline, items };
}

// The whole tool: one shared input feeds both the decoded view and the chain.
function Toolkit() {
  // decode side
  const [results, setResults] = React.useState(null);
  const [decodeError, setDecodeError] = React.useState(null);
  // chain side
  const [pemText, setPemText] = React.useState('');
  const [fetchedPem, setFetchedPem] = React.useState('');
  const [chainResult, setChainResult] = React.useState(null);
  const [chainError, setChainError] = React.useState(null);
  const [selectedOpt, setSelectedOpt] = React.useState(0);
  const [fetching, setFetching] = React.useState(false);
  const [enriched, setEnriched] = React.useState(false); // backend discovery finished for this input?
  // shared
  const [inputError, setInputError] = React.useState(null); // backend/fetch problems from the input
  const [busy, setBusy] = React.useState(false);
  const [backendUp, setBackendUp] = React.useState(null);
  const initialView = (location.hash || '').replace('#', '') === 'chain' ? 'chain' : 'decoded';
  const [view, setView] = React.useState(initialView);
  const attempted = React.useRef(new Set());
  const enrichedFor = React.useRef(null); // input we've already pulled alternatives for

  React.useEffect(() => { window.CertAPI.probe().then(setBackendUp); }, []);
  React.useEffect(() => { location.hash = view; }, [view]);

  function rebuildChain(text, extra) {
    try {
      const res = window.CertLib.buildChain(text, extra);
      setChainResult(res); setChainError(null);
      // Default to the chain the user actually supplied/serves ("in use") so the
      // selection matches the health verdict; otherwise the recommended one.
      const cur = res.options.findIndex((o) => o.current);
      setSelectedOpt(cur >= 0 ? cur : 0);
    } catch (e) { setChainResult(null); setChainError(e.message); }
  }

  // single entry point: one input -> decode + chain
  function handleText(text) {
    setInputError(null);
    setPemText(text); setFetchedPem(''); attempted.current = new Set(); enrichedFor.current = null; setEnriched(false);
    try { setResults(window.CertLib.decodeText(text)); setDecodeError(null); }
    catch (e) { setResults(null); setDecodeError(e.message); }
    rebuildChain(text, '');
  }

  function handleClear() {
    setResults(null); setDecodeError(null); setInputError(null);
    setPemText(''); setFetchedPem(''); setChainResult(null); setChainError(null);
    attempted.current = new Set(); enrichedFor.current = null; setSelectedOpt(0); setEnriched(false);
  }

  function autoFetch() {
    const src = pemText + '\n' + fetchedPem;
    setFetching(true);
    return window.CertAPI.completeChain(src)
      .then((res) => {
        const got = res.fetched || [];
        if (got.length) {
          const nf = (fetchedPem ? fetchedPem + '\n' : '') + got.join('\n');
          setFetchedPem(nf); rebuildChain(pemText, nf);
        }
      })
      .catch((e) => { if (!window.CertAPI.isBackendError(e)) setChainError('Fetch failed: ' + e.message); })
      .finally(() => { setFetching(false); setEnriched(true); });
  }

  // Discover the full picture from the backend, once per input: this both
  // completes a missing chain AND surfaces alternative cross-signed options even
  // when the supplied chain is already complete (so the user can see, and the
  // "In use" one is marked). Then guard against re-fetching.
  React.useEffect(() => {
    if (!chainResult || backendUp !== true || fetching) return;
    const leafFp = chainResult.leaf ? chainResult.leaf.fingerprints.sha256 : pemText;
    if (enrichedFor.current !== leafFp) {
      enrichedFor.current = leafFp;
      autoFetch();
      return;
    }
    // after enrichment, chase any still-missing intermediate (guarded)
    const gap = chainResult.options.find((o) => o.missing && o.missing.aia && o.missing.aia.length);
    if (!gap) return;
    const key = gap.missing.aia[0] || (gap.missing.issuerDN && gap.missing.issuerDN.str) || '';
    if (!key || attempted.current.has(key)) return;
    attempted.current.add(key);
    autoFetch();
  }, [chainResult, backendUp, fetching]);

  // health verdict of the provided chain — shown on both tabs. Structural
  // correctness comes from analyzeChain; optimality (is the supplied chain the
  // most-compatible option?) is folded in from the discovered chain options.
  // While the backend is still discovering alternatives, don't assert an "ok"
  // verdict yet — optimality is unknown, so show a transient "checking" state
  // rather than flashing "correct and optimal" and then downgrading.
  const baseHealth = results ? window.CertLib.analyzeChain(results) : null;
  let health;
  if (baseHealth && baseHealth.level === 'ok' && backendUp !== false && !enriched) {
    health = { level: 'pending', headline: 'Checking the chain…', items: [{ level: 'info', text: 'Looking for the most compatible chain…' }] };
  } else {
    health = withOptimality(baseHealth, chainResult);
  }

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-6)' }}>
      <ToolkitInput onText={handleText} onClear={handleClear} onError={setInputError} busy={busy} setBusy={setBusy} />
      {busy && <div style={{ font: 'var(--body-sm)', color: 'var(--text-muted)' }}>Working…</div>}
      {inputError === 'BACKEND_OFFLINE' && (
        <Notice tone="warning" title="Backend offline">
          Live fetch needs the bundled <code style={{ fontFamily: 'var(--font-mono)' }}>server.js</code> running. Paste or upload the certificate instead.
        </Notice>
      )}
      {inputError && inputError !== 'BACKEND_OFFLINE' && <Notice tone="danger" title="Could not fetch">{inputError}</Notice>}

      {/* the two outputs, below the single input */}
      <Tabs value={view} onChange={setView} items={[
        { value: 'decoded', label: 'Decoded' },
        { value: 'chain', label: 'Certificate chain' + (chainResult && chainResult.options.length > 1 ? ' (' + chainResult.options.length + ' options)' : '') },
      ]} />

      {view === 'decoded'
        ? <DecodedView results={results} error={decodeError} health={health} />
        : <ChainView result={chainResult} error={chainError} health={health} selected={selectedOpt} onSelect={setSelectedOpt}
            onFetch={autoFetch} fetching={fetching} backendUp={backendUp} />}
    </div>
  );
}

function App() {
  return (
    <div style={{ minHeight: '100vh', background: 'var(--surface-page)' }}>
      <Header />
      <main style={{ maxWidth: 'var(--container-max)', margin: '0 auto', padding: 'var(--space-8)' }}>
        <div style={{ marginBottom: 'var(--space-6)' }}>
          <SectionIntro title="Decode certificates & build the chain"
            body="Paste PEM, upload a .pem/.crt/.cer/.der file, or fetch live from a host — once. Every field is decoded in your browser, and the complete, validated certificate chain is built from the same input." />
        </div>
        <Toolkit />
      </main>
    </div>
  );
}

function SectionIntro({ title, body }) {
  return (
    <div style={{ maxWidth: 760 }}>
      <h1 style={{ margin: 0, font: 'var(--h2)', color: 'var(--text-strong)', letterSpacing: 'var(--tracking-tight)' }}>{title}</h1>
      <p style={{ margin: '8px 0 0', font: 'var(--body-lg)', color: 'var(--text-body)', textWrap: 'pretty' }}>{body}</p>
    </div>
  );
}

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