/* chain.jsx — Certificate chain builder tab. */
const chDS = window.DeltaBlueDesignSystem_b0e437;
const { Button: CButton, Card: CCard, Badge: CBadge, Switch: CSwitch } = chDS;

const ROLE_META = {
  leaf: { label: 'Your certificate', sub: 'Leaf / end-entity', tone: 'brand' },
  intermediate: { label: 'Intermediate CA', sub: 'Signs the level below', tone: 'neutral' },
  root: { label: 'Root CA', sub: 'Trust anchor', tone: 'success' },
};

// e.g. "21 Mar 2036"
const MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
function fmtDate(d) {
  if (!(d instanceof Date) || isNaN(d)) return '';
  return d.getUTCDate() + ' ' + MONTHS[d.getUTCMonth()] + ' ' + d.getUTCFullYear();
}

function Connector({ ok }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', padding: '2px 0' }}>
      <div style={{ width: 2, height: 14, background: ok === false ? 'var(--amber-500)' : 'var(--border-strong)' }} />
      <div style={{ font: 'var(--caption)', textTransform: 'uppercase', letterSpacing: 'var(--tracking-eyebrow)',
        color: ok === false ? 'var(--amber-600)' : 'var(--text-subtle)', display: 'flex', alignItems: 'center', gap: 5 }}>
        {ok === false ? <Icon.Alert size={12}/> : null} signed by
      </div>
      <div style={{ width: 2, height: 14, background: ok === false ? 'var(--amber-500)' : 'var(--border-strong)' }} />
    </div>
  );
}

function ChainCardNode({ node }) {
  const meta = ROLE_META[node.role] || ROLE_META.intermediate;
  const c = node.cert;
  return (
    <CCard elevation="sm" padding="md">
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 'var(--space-3)' }}>
        <div style={{ minWidth: 0 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
            <CBadge tone={meta.tone} variant={node.role === 'leaf' ? 'solid' : 'soft'} size="sm">{meta.label}</CBadge>
            {!c.selfSigned && <StatusPill status={c.status} size="sm" />}
          </div>
          <div style={{ font: 'var(--body)', fontWeight: 600, color: 'var(--text-strong)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
            {c.subject.cn || c.subject.str}
          </div>
          <div style={{ font: 'var(--body-sm)', color: 'var(--text-muted)', marginTop: 2 }}>
            {meta.sub}{c.publicKey && c.publicKey.algorithm ? ' · ' + c.publicKey.algorithm : ''}
          </div>
          {c.notAfter && (
            <div style={{ font: 'var(--body-sm)', marginTop: 4, display: 'flex', alignItems: 'center', gap: 5,
              color: c.status === 'expired' ? 'var(--red-600, var(--danger-fg))' : c.status === 'expiring' ? 'var(--amber-600)' : 'var(--text-muted)' }}>
              <Icon.Clock size={13}/>
              {c.status === 'expired' ? 'Expired ' : 'Expires '}{fmtDate(c.notAfter)}
              {c.daysLeft != null && c.status !== 'expired' ? ' · ' + c.daysLeft + ' day' + (c.daysLeft === 1 ? '' : 's') + ' left' : ''}
            </div>
          )}
        </div>
        <div style={{ flex: 'none', color: node.role === 'leaf' ? 'var(--brand)' : node.role === 'root' ? 'var(--green-500)' : 'var(--text-subtle)' }}>
          {node.role === 'root' ? <Icon.Shield size={20}/> : node.role === 'leaf' ? <Icon.Globe size={20}/> : <Icon.Server size={20}/>}
        </div>
      </div>
    </CCard>
  );
}

// Terminal root / trust-anchor node — shown when the chain doesn't itself end
// in a self-signed root (e.g. cross-signed chains). The anchor cert lives in
// client trust stores rather than the served bundle, so it's drawn dashed.
function AnchorNode({ label }) {
  return (
    <CCard elevation="flat" padding="md" style={{ border: '1.5px dashed var(--border-strong)', background: 'var(--surface-sunken)' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 'var(--space-3)' }}>
        <div style={{ minWidth: 0 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
            <CBadge tone="success" variant="soft" size="sm">Root CA</CBadge>
            <CBadge tone="neutral" variant="soft" size="sm">Trust anchor</CBadge>
          </div>
          <div style={{ font: 'var(--body)', fontWeight: 600, color: 'var(--text-strong)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{label}</div>
          <div style={{ font: 'var(--body-sm)', color: 'var(--text-muted)', marginTop: 2 }}>Held in client trust stores — not part of the served bundle</div>
        </div>
        <div style={{ flex: 'none', color: 'var(--green-500)' }}><Icon.Shield size={20}/></div>
      </div>
    </CCard>
  );
}

function MissingNode({ missing, onFetch, fetching, backendUp }) {
  const url = (missing.aia && missing.aia[0]) || '';
  return (
    <CCard elevation="flat" padding="md" style={{ border: '1.5px dashed var(--amber-500)', background: 'var(--warning-bg)' }}>
      <div style={{ display: 'flex', gap: 'var(--space-3)' }}>
        <div style={{ color: 'var(--amber-600)', flex: 'none' }}><Icon.Alert size={20}/></div>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ font: 'var(--body)', fontWeight: 600, color: 'var(--text-strong)' }}>
            {fetching ? 'Fetching missing intermediate…' : 'Missing intermediate'}
          </div>
          <div style={{ font: 'var(--body-sm)', color: 'var(--text-body)', marginTop: 2 }}>
            Issuer not in your bundle: <strong>{missing.issuerDN.cn || missing.issuerDN.str}</strong>
          </div>
          {url ? (
            <div style={{ marginTop: 'var(--space-3)' }}>
              <div style={{ font: 'var(--caption)', textTransform: 'uppercase', letterSpacing: 'var(--tracking-eyebrow)', color: 'var(--text-muted)', marginBottom: 4 }}>AIA · CA Issuers</div>
              <div style={{ font: 'var(--body-sm)', fontFamily: 'var(--font-mono)', fontSize: 12, wordBreak: 'break-all', color: 'var(--brand-ink)', marginBottom: 'var(--space-3)' }}>{url}</div>
              {backendUp !== false && (
                <div style={{ font: 'var(--body-sm)', color: 'var(--text-muted)', marginBottom: 'var(--space-3)', display: 'flex', alignItems: 'center', gap: 6 }}>
                  {fetching
                    ? <><Icon.Clock size={14}/> Downloading and adding it automatically…</>
                    : 'Fetched automatically when reachable.'}
                </div>
              )}
              <div style={{ display: 'flex', gap: 'var(--space-2)', flexWrap: 'wrap' }}>
                <Button variant="outline" size="sm" iconLeft={<Icon.Download size={15}/>} onClick={onFetch} disabled={fetching}>
                  {fetching ? 'Fetching…' : 'Retry fetch'}
                </Button>
                <Button variant="ghost" size="sm" iconLeft={<Icon.Globe size={15}/>} onClick={() => window.open(url, '_blank')}>Open URL</Button>
              </div>
              {backendUp === false && (
                <div style={{ marginTop: 'var(--space-2)', font: 'var(--body-sm)', color: 'var(--amber-600)' }}>
                  Auto-fetch needs <code style={{ fontFamily: 'var(--font-mono)' }}>server.js</code> running. Otherwise download the file above, convert to PEM, and paste it in.
                </div>
              )}
            </div>
          ) : (
            <div style={{ font: 'var(--body-sm)', color: 'var(--text-muted)', marginTop: 'var(--space-2)' }}>
              No AIA URL in the certificate — locate this CA's intermediate from your provider and paste it above.
            </div>
          )}
        </div>
      </div>
    </CCard>
  );
}

// Chain output view — fed by the shared input in the parent toolkit. All chain
// state (auto-fetch, options, selection) is owned by the parent and passed in.
function ChainView({ result, error, health, selected, onSelect, onFetch, fetching, backendUp }) {
  if (error) {
    return <Notice tone="danger" title="Could not build a chain">{error} — paste at least your server (leaf) certificate.</Notice>;
  }
  if (!result) {
    return (
      <CCard elevation="flat" padding="md">
        <div style={{ textAlign: 'center', padding: 'var(--space-10) var(--space-4)', color: 'var(--text-muted)' }}>
          <div style={{ display: 'flex', justifyContent: 'center', marginBottom: 'var(--space-3)', color: 'var(--text-subtle)' }}><Icon.Link size={36}/></div>
          <div style={{ font: 'var(--body)', color: 'var(--text-body)' }}>Analyze a certificate to see how it links to its trusted root —</div>
          <div style={{ font: 'var(--body-sm)', marginTop: 6 }}>and get the exact bundle to install on your server.</div>
        </div>
      </CCard>
    );
  }
  return (
    <div style={{ minWidth: 0 }}>
      {health && (
        <div style={{ marginBottom: 'var(--space-4)' }}>
          <ChainHealth report={health} />
        </div>
      )}
      {backendUp === false && (
        <div style={{ marginBottom: 'var(--space-4)' }}>
          <Notice tone="info" title="Working offline">
            Assembling &amp; validating pasted certs runs fully in your browser. Auto-downloading missing intermediates needs the bundled <code style={{ fontFamily: 'var(--font-mono)' }}>server.js</code>.
          </Notice>
        </div>
      )}
      <ChainResult result={result} selected={selected} onSelect={onSelect}
        onFetch={onFetch} fetching={fetching} backendUp={backendUp} health={health} />
    </div>
  );
}

// One selectable chain option in the picker (shown when a cert has cross-signed
// alternatives, e.g. a root that is also signed by an older, more compatible CA).
const OPT_TAG = {
  compatible: { label: 'Most compatible', tone: 'success' },
  modern: { label: 'Most modern', tone: 'brand' },
};
function OptionTab({ opt, active, onClick, inUseLevel }) {
  const t = opt.terminus;
  const tone = active ? 'var(--brand)' : 'var(--border-strong)';
  const desc = t.type === 'root' ? 'self-signed root'
    : t.type === 'missing' ? 'incomplete' : '→ ' + t.label;
  const tag = OPT_TAG[opt.tag];
  return (
    <button onClick={onClick} style={{
      textAlign: 'left', cursor: 'pointer', flex: '1 1 200px', minWidth: 0,
      padding: 'var(--space-3) var(--space-4)', borderRadius: 'var(--radius-md)',
      border: (active ? '1.5px solid var(--brand)' : opt.recommended ? '1.5px solid var(--green-500)' : '1.5px solid ' + tone),
      background: active ? 'var(--brand-bg, color-mix(in srgb, var(--brand) 8%, transparent))' : 'var(--surface-card)',
      outline: 'none' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap', marginBottom: 4 }}>
        {tag && <CBadge tone={tag.tone} variant="soft" size="sm">{tag.label}</CBadge>}
        {opt.recommended && <CBadge tone="success" variant="solid" size="sm">Recommended</CBadge>}
        {opt.current && (
          <CBadge tone={inUseLevel === 'error' ? 'danger' : inUseLevel === 'warning' ? 'warning' : 'brand'} variant="soft" size="sm">
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 3 }}>
              {(inUseLevel === 'warning' || inUseLevel === 'error') && <Icon.Alert size={11}/>}In use
            </span>
          </CBadge>
        )}
      </div>
      <div style={{ font: 'var(--caption)', textTransform: 'uppercase', letterSpacing: 'var(--tracking-eyebrow)',
        color: active ? 'var(--brand)' : 'var(--text-muted)', marginBottom: 2 }}>
        {opt.interCount} intermediate{opt.interCount === 1 ? '' : 's'}{opt.hasRoot ? ' + root' : ''}
      </div>
      <div style={{ font: 'var(--body-sm)', fontWeight: 600, color: 'var(--text-strong)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
        {t.type === 'root' ? 'Ends at root' : t.type === 'missing' ? 'Missing issuer' : 'Chains to'}
      </div>
      <div style={{ font: 'var(--body-sm)', color: 'var(--text-muted)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{desc}</div>
      {opt.caExpiry && (
        <div style={{ font: 'var(--body-sm)', color: 'var(--text-subtle)', marginTop: 2, display: 'flex', alignItems: 'center', gap: 4, whiteSpace: 'nowrap' }}>
          <Icon.Clock size={12}/> CA until {fmtDate(opt.caExpiry)}
        </div>
      )}
    </button>
  );
}

function ChainResult({ result, selected, onSelect, onFetch, fetching, backendUp, health }) {
  const inUseLevel = health && (health.level === 'warning' || health.level === 'error') ? health.level : null;
  const options = result.options || [];
  const idx = Math.min(selected || 0, options.length - 1);
  const opt = options[idx] || options[0];

  const [includeLeaf, setIncludeLeaf] = React.useState(false);
  const [includeRoot, setIncludeRoot] = React.useState(false);

  if (!opt) return null;
  const complete = opt.hasRoot && !opt.missing;
  const hasInter = opt.interCount > 0;

  // assemble the PEM bundle from the selected option's parts per the toggles
  const showRoot = opt.canIncludeRoot && includeRoot;
  let chainPem = opt.parts
    .filter((p) => p.role === 'leaf' ? includeLeaf : p.role === 'root' ? showRoot : true)
    .map((p) => p.pem).join('\n');
  // cross-signed chains have no root node in the path; append the available one
  if (showRoot && !opt.hasRoot && opt.rootPem) chainPem = (chainPem ? chainPem + '\n' : '') + opt.rootPem;
  const fileName = includeLeaf ? 'fullchain.pem' : showRoot ? 'chain-with-root.pem' : 'chain.pem';

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-5)' }}>
      {options.length > 1 && (
        <SectionCard icon={<Icon.Link size={15}/>} title={'Chain options (' + options.length + ')'}>
          <div style={{ font: 'var(--body-sm)', color: 'var(--text-muted)', marginBottom: 'var(--space-3)' }}>
            This certificate can be chained more than one way — its issuer is cross-signed by multiple roots. <strong>Most compatible</strong> reaches the oldest, most widely-distributed root (best for old clients); <strong>Most modern</strong> is the shortest chain to the newest root. When in doubt, use the recommended one.
          </div>
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 'var(--space-2)' }}>
            {options.map((o, i) => (
              <OptionTab key={i} opt={o} active={i === idx} onClick={() => onSelect(i)}
                inUseLevel={o.current ? inUseLevel : null} />
            ))}
          </div>
        </SectionCard>
      )}

      <div>
        {opt.missing ? (
          <Notice tone="warning" title="Chain incomplete">This chain is missing an intermediate. {backendUp === false ? 'Download it from the AIA URL below and paste it in.' : 'Fetching it automatically…'}</Notice>
        ) : complete ? (
          <Notice tone="success" icon={<Icon.Check size={18}/>} title="Chain complete">Leaf → {opt.ordered.length - 2 >= 1 ? (opt.ordered.length - 2) + ' CA' + (opt.ordered.length - 2 === 1 ? '' : 's') + ' → ' : ''}self-signed root. Ready to install.</Notice>
        ) : (
          <Notice tone="info" title="Chain assembled">{opt.ordered.length} certificate{opt.ordered.length === 1 ? '' : 's'} ordered, chaining to <strong>{opt.terminus.label}</strong>. No self-signed root present — normal for a server bundle (the client's trust store holds the root).</Notice>
        )}
      </div>

      <SectionCard icon={<Icon.Link size={15}/>} title="Chain of trust">
        <div>
          {opt.ordered.map((node, i) => (
            <React.Fragment key={i}>
              <ChainCardNode node={node} />
              {i < opt.ordered.length - 1 && <Connector ok={node.issuerMatch} />}
              {i === opt.ordered.length - 1 && node.needsIssuer && <Connector ok={false} />}
            </React.Fragment>
          ))}
          {opt.missing && (
            <MissingNode missing={opt.missing} onFetch={onFetch} fetching={fetching} backendUp={backendUp} />
          )}
          {!opt.missing && !opt.hasRoot && (
            <>
              <Connector ok={true} />
              <AnchorNode label={opt.terminus.label} />
            </>
          )}
        </div>
      </SectionCard>

      <SectionCard icon={<Icon.Server size={15}/>} title="Certificate chain (PEM)"
        right={
          <div style={{ display: 'flex', gap: 'var(--space-4)', flexWrap: 'wrap', alignItems: 'center' }}>
            <CSwitch checked={includeLeaf} onChange={setIncludeLeaf} label="Include leaf certificate" />
            <CSwitch checked={showRoot} onChange={setIncludeRoot} disabled={!opt.canIncludeRoot} label="Include root certificate" />
          </div>
        }>
        <div style={{ font: 'var(--body-sm)', color: 'var(--text-muted)', marginBottom: 'var(--space-3)' }}>
          {includeLeaf
            ? 'Your leaf certificate first, then each intermediate' + (showRoot ? ', then the self-signed root' : '') + ' — a single "fullchain" file for servers that want it (e.g. nginx ssl_certificate).'
            : 'Intermediate CA certificate(s) only, in order — the chain file most servers reference separately (e.g. Apache SSLCertificateChainFile). Toggle on to prepend your leaf.'}
          {opt.canIncludeRoot
            ? (showRoot
                ? (opt.rootInfo ? ' Root appended: ' + opt.rootInfo.cn + '.' : '')
                : ' Including the root is optional — usually not needed or recommended (clients hold the root), handy for trust-store imports and testing.')
            : ' This chain ends at an external root (' + opt.terminus.label + ') that isn’t in the bundle, so there’s no root certificate available to include.'}
        </div>
        {chainPem ? (
          <CodeBlock value={chainPem} filename={fileName} downloadLabel={'Download ' + fileName} />
        ) : (
          <Notice tone="info" title={hasInter ? 'Nothing to output' : 'No intermediates in this chain'}>
            {hasInter
              ? 'Toggle “Include leaf certificate” to build a bundle.'
              : 'This chain has no intermediate certificates. Turn on “Include leaf certificate” to output the leaf on its own.'}
          </Notice>
        )}
      </SectionCard>
    </div>
  );
}

window.ChainView = ChainView;
window.ChainResult = ChainResult;
