/* decoder.jsx — Certificate / CSR decoder tab. */
const decDS = window.DeltaBlueDesignSystem_b0e437;
const { Button: DButton, Card: DCard, Badge: DBadge, Tag: DTag } = decDS;

function Segmented({ value, onChange, items }) {
  return (
    <div style={{ display: 'inline-flex', background: 'var(--surface-sunken)', border: '1px solid var(--border)', borderRadius: 'var(--radius-md)', padding: 3, gap: 2 }}>
      {items.map((it) => {
        const active = it.value === value;
        return (
          <button key={it.value} onClick={() => onChange(it.value)} style={{
            display: 'inline-flex', alignItems: 'center', gap: 6, border: 'none', cursor: 'pointer',
            font: 'var(--body-sm)', fontWeight: 500, padding: '7px 12px', borderRadius: 'var(--radius-sm)',
            background: active ? 'var(--surface-card)' : 'transparent',
            color: active ? 'var(--text-strong)' : 'var(--text-muted)',
            boxShadow: active ? 'var(--shadow-xs)' : 'none',
            transition: 'all var(--dur-fast) var(--ease-out)',
          }}>{it.icon}{it.label}</button>
        );
      })}
    </div>
  );
}

function fieldInput(props) {
  return <input {...props} style={Object.assign({
    width: '100%', font: 'var(--body)', padding: '10px 12px', borderRadius: 'var(--radius-md)',
    border: '1px solid var(--border-strong)', background: 'var(--surface-card)', color: 'var(--text-strong)',
    outline: 'none', fontFamily: 'var(--font-sans)',
  }, props.style)} />;
}

// Shared input for the whole toolkit. Reports the raw PEM text up via onText();
// the parent decodes it AND builds the chain from the single input.
function ToolkitInput({ onText, onClear, onError, busy, setBusy }) {
  const [mode, setMode] = React.useState('paste');
  const [pem, setPem] = React.useState('');
  const [host, setHost] = React.useState('');
  const [port, setPort] = React.useState('443');
  const [backendUp, setBackendUp] = React.useState(null);

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

  function submit(text) { onError(null); onText(text); }

  function onFile(file) {
    const reader = new FileReader();
    reader.onload = () => {
      onError(null);
      try {
        // normalise to PEM text so both decode + chain consume one thing
        const u8 = new Uint8Array(reader.result);
        let s = ''; for (let i = 0; i < u8.length; i++) s += String.fromCharCode(u8[i]);
        let text;
        if (/-----BEGIN/.test(s)) text = s;
        else {
          const parsed = window.CertLib.decodeFile(reader.result);
          text = parsed.map((r) => window.CertLib.toPem(r.der, r.kind === 'csr' ? 'CERTIFICATE REQUEST' : 'CERTIFICATE')).join('\n');
        }
        setPem(text); setMode('paste'); submit(text);
      } catch (e) { onError(e.message); }
    };
    reader.readAsArrayBuffer(file);
  }

  function fetchHost() {
    if (!host.trim()) return;
    setBusy(true); onError(null);
    window.CertAPI.fetchServerCert(host.trim(), parseInt(port, 10) || 443)
      .then((res) => {
        const all = (res.chain || []).join('\n');
        setPem(all); setMode('paste'); submit(all);
      })
      .catch((e) => {
        if (window.CertAPI.isBackendError(e)) onError('BACKEND_OFFLINE');
        else onError('Could not fetch from ' + host + ': ' + e.message);
      })
      .finally(() => setBusy(false));
  }

  function loadSample() {
    setBusy(true); onError(null);
    window.CertLib.makeSampleChain((s) => {
      setPem(s.full); setMode('paste'); submit(s.full); setBusy(false);
    });
  }

  return (
    <DCard elevation="sm" padding="md">
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 'var(--space-3)', marginBottom: 'var(--space-4)', flexWrap: 'wrap' }}>
        <Segmented value={mode} onChange={setMode} items={[
          { value: 'paste', label: 'Paste', icon: <Icon.File size={15}/> },
          { value: 'upload', label: 'Upload', icon: <Icon.Upload size={15}/> },
          { value: 'host', label: 'Fetch from host', icon: <Icon.Globe size={15}/> },
        ]} />
        <Button variant="ghost" size="sm" onClick={loadSample} disabled={busy}>Load sample</Button>
      </div>

      {mode === 'paste' && (
        <div>
          <textarea value={pem} onChange={(e) => setPem(e.target.value)} spellCheck={false}
            placeholder={"Paste a certificate or CSR in PEM format\n\n-----BEGIN CERTIFICATE-----\nMIIF...\n-----END CERTIFICATE-----"}
            style={{ width: '100%', minHeight: 440, resize: 'vertical', font: 'var(--body-sm)', fontFamily: 'var(--font-mono)',
              fontSize: '12.5px', lineHeight: 1.5, padding: 'var(--space-4)', borderRadius: 'var(--radius-md)', whiteSpace: 'pre',
              border: '1px solid var(--border-strong)', background: 'var(--surface-card)', color: 'var(--text-strong)', outline: 'none' }} />
          <div style={{ marginTop: 'var(--space-4)', display: 'flex', gap: 'var(--space-3)' }}>
            <Button variant="primary" iconLeft={<Icon.Search size={16}/>} onClick={() => submit(pem)} disabled={!pem.trim() || busy}>Analyze</Button>
            <Button variant="ghost" onClick={() => { setPem(''); onClear(); }} disabled={!pem}>Clear</Button>
          </div>
        </div>
      )}

      {mode === 'upload' && <Dropzone onFile={onFile} />}

      {mode === 'host' && (
        <div>
          <div style={{ display: 'flex', gap: 'var(--space-3)', alignItems: 'flex-end' }}>
            <label style={{ flex: 1, font: 'var(--body-sm)', color: 'var(--text-muted)', fontWeight: 500 }}>
              <div style={{ marginBottom: 6 }}>Hostname</div>
              {fieldInput({ value: host, placeholder: 'example.com', onChange: (e) => setHost(e.target.value), onKeyDown: (e) => e.key === 'Enter' && fetchHost() })}
            </label>
            <label style={{ width: 110, font: 'var(--body-sm)', color: 'var(--text-muted)', fontWeight: 500 }}>
              <div style={{ marginBottom: 6 }}>Port</div>
              {fieldInput({ value: port, onChange: (e) => setPort(e.target.value.replace(/[^0-9]/g, '')) })}
            </label>
            <Button variant="primary" iconLeft={<Icon.Server size={16}/>} onClick={fetchHost} disabled={!host.trim() || busy}>{busy ? 'Connecting…' : 'Fetch'}</Button>
          </div>
          <div style={{ marginTop: 'var(--space-3)' }}>
            {backendUp === false && (
              <Notice tone="warning" title="Backend not running">
                Live host fetch needs the bundled <code style={{ fontFamily: 'var(--font-mono)' }}>server.js</code> (browsers can't open raw TLS sockets).
                Run <code style={{ fontFamily: 'var(--font-mono)' }}>node server.js</code> and reload, or paste the certificate instead.
              </Notice>
            )}
            {backendUp === true && (
              <div style={{ font: 'var(--body-sm)', color: 'var(--text-muted)' }}>
                Connects over TLS and decodes the certificate the server presents, including the chain it sends.
              </div>
            )}
          </div>
        </div>
      )}
    </DCard>
  );
}

/* ---------- results ---------- */
function tagRow(items, tone) {
  if (!items || !items.length) return <span style={{ color: 'var(--text-subtle)', font: 'var(--body-sm)' }}>None</span>;
  return (
    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 'var(--space-2)' }}>
      {items.map((v, i) => <DBadge key={i} tone={tone || 'neutral'} size="sm">{v}</DBadge>)}
    </div>
  );
}

// role badge shown on the cert summary / collapsed header
const ROLE_BADGE = {
  leaf: { label: 'Leaf', tone: 'brand' },
  intermediate: { label: 'Intermediate CA', tone: 'neutral' },
  root: { label: 'Root CA', tone: 'success' },
};

// classify decoded certs into leaf / intermediate / root / csr (by input order)
function rolesForResults(results) {
  return results.map((c) => {
    if (c.kind === 'csr') return 'csr';
    if (!c.meta.isCA) return 'leaf';
    if (c.selfSigned) return 'root';
    return 'intermediate';
  });
}

// the badge/title row shared by the expanded summary and the collapsed header
function CertSummary({ cert, role }) {
  const isCsr = cert.kind === 'csr';
  const rb = ROLE_BADGE[role];
  return (
    <div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6, flexWrap: 'wrap' }}>
        <DBadge tone="brand" variant="solid" size="sm">{isCsr ? 'CSR' : 'X.509 Certificate'}</DBadge>
        {rb && <DBadge tone={rb.tone} size="sm">{rb.label}</DBadge>}
        {!isCsr && <StatusPill status={cert.status} />}
        {!isCsr && cert.selfSigned && <DBadge tone="neutral" size="sm">Self-signed</DBadge>}
      </div>
      <div style={{ font: 'var(--h3)', color: 'var(--text-strong)', letterSpacing: 'var(--tracking-tight)' }}>
        {cert.subject.cn || cert.subject.str || '(no common name)'}
      </div>
      {!isCsr && <div style={{ font: 'var(--body-sm)', color: 'var(--text-muted)', marginTop: 4 }}>Issued by {cert.issuer.cn || cert.issuer.str}</div>}
    </div>
  );
}

function CertDetails({ cert }) {
  const isCsr = cert.kind === 'csr';
  const k = cert.publicKey || {};
  const m = cert.meta || {};
  const sanDns = (m.san || []).map((s) => (s.type === 'DNS' ? s.value : s.type + ':' + s.value));

  const validityValue = cert.notBefore && cert.notAfter ? (
    <span>
      {cert.notBefore.toUTCString()} &nbsp;→&nbsp; {cert.notAfter.toUTCString()}
      {cert.daysLeft != null && (
        <div style={{ marginTop: 6 }}>
          <DBadge tone={cert.status === 'expired' ? 'danger' : cert.status === 'expiring' ? 'warning' : 'success'} size="sm">
            {cert.daysLeft >= 0 ? cert.daysLeft + ' days remaining' : Math.abs(cert.daysLeft) + ' days ago'}
          </DBadge>
        </div>
      )}
    </span>
  ) : null;

  return (
      <div className="dec-masonry">
      <SectionCard icon={<Icon.Shield size={15}/>} title="Subject">
        <DefList rows={cert.subject.parts.map((p) => ({ label: p.label, value: p.value }))} />
      </SectionCard>

      {!isCsr && (
        <SectionCard icon={<Icon.Shield size={15}/>} title="Issuer">
          <DefList rows={cert.issuer.parts.map((p) => ({ label: p.label, value: p.value }))} />
        </SectionCard>
      )}

      {!isCsr && (
        <SectionCard icon={<Icon.Clock size={15}/>} title="Validity">
          <DefList rows={[
            { label: 'Not before', value: cert.notBefore ? cert.notBefore.toUTCString() : null },
            { label: 'Not after', value: cert.notAfter ? cert.notAfter.toUTCString() : null },
            { label: 'Status', value: validityValue },
          ]} />
        </SectionCard>
      )}

      <SectionCard icon={<Icon.Globe size={15}/>} title="Subject Alternative Names">
        {tagRow(sanDns, 'brand')}
      </SectionCard>

      <SectionCard icon={<Icon.Key size={15}/>} title="Public Key">
        <DefList rows={[
          { label: 'Algorithm', value: k.algorithm || k.keyType },
          { label: 'Key size', value: k.bits ? k.bits + ' bits' : null },
          k.curve ? { label: 'Curve', value: k.curve } : null,
          k.exponent ? { label: 'Exponent', value: '0x' + k.exponent.toString(16) + ' (' + k.exponent + ')' } : null,
          k.modulusHex ? { label: 'Modulus', value: k.modulusHex, mono: true, break: true } : null,
        ]} />
      </SectionCard>

      <SectionCard icon={<Icon.File size={15}/>} title={isCsr ? 'Signature' : 'Signature & serial'}>
        <DefList rows={[
          { label: 'Signature algorithm', value: cert.signatureAlgorithm },
          !isCsr ? { label: 'Serial number', value: cert.serial, mono: true, break: true } : null,
          !isCsr ? { label: 'Version', value: 'v' + cert.version } : null,
        ]} />
      </SectionCard>

      {!isCsr && (
        <SectionCard icon={<Icon.Fingerprint size={15}/>} title="Fingerprints">
          <DefList rows={[
            { label: 'SHA-256', value: cert.fingerprints.sha256, mono: true, break: true },
            { label: 'SHA-1', value: cert.fingerprints.sha1, mono: true, break: true },
          ]} />
        </SectionCard>
      )}
      {isCsr && (
        <SectionCard icon={<Icon.Fingerprint size={15}/>} title="Fingerprint">
          <DefList rows={[{ label: 'SHA-256', value: cert.fingerprints.sha256, mono: true, break: true }]} />
        </SectionCard>
      )}

      {!isCsr && cert.extensions && cert.extensions.length > 0 && (
        <SectionCard icon={<Icon.Link size={15}/>} title="Extensions">
          <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-3)' }}>
            {cert.extensions.map((ext, i) => (
              <div key={i} style={{ paddingBottom: 'var(--space-3)', borderBottom: i < cert.extensions.length - 1 ? '1px solid var(--border)' : 'none' }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
                  <span style={{ font: 'var(--body-sm)', fontWeight: 600, color: 'var(--text-strong)' }}>{ext.name}</span>
                  {ext.critical && <DBadge tone="warning" size="sm">critical</DBadge>}
                </div>
                {ext.list ? (
                  <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
                    {ext.list.map((v, j) => <DBadge key={j} tone="neutral" size="sm">{v}</DBadge>)}
                  </div>
                ) : (
                  <div style={{ font: 'var(--body-sm)', color: 'var(--text-body)', fontFamily: 'var(--font-mono)', fontSize: 12, wordBreak: 'break-all' }}>{ext.value}</div>
                )}
              </div>
            ))}
          </div>
        </SectionCard>
      )}
      </div>
  );
}

function CertResult({ cert, role }) {
  return (
    <div>
      <DCard elevation="md" padding="md" style={{ marginBottom: 'var(--space-5)' }}>
        <CertSummary cert={cert} role={role} />
      </DCard>
      <CertDetails cert={cert} />
    </div>
  );
}

// Collapsible cert card — used when a multi-cert chain is decoded. The leaf is
// expanded by default; intermediates and the root collapse to a one-line header.
function CollapsibleCert({ cert, role, defaultOpen }) {
  const [open, setOpen] = React.useState(!!defaultOpen);
  const isCsr = cert.kind === 'csr';
  const rb = ROLE_BADGE[role];
  return (
    <DCard elevation="sm" padding="md">
      <div role="button" tabIndex={0} onClick={() => setOpen(!open)}
        onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setOpen(!open); } }}
        style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-3)', cursor: 'pointer' }}>
        <span style={{ display: 'flex', color: 'var(--text-subtle)', transform: open ? 'rotate(90deg)' : 'none', transition: 'transform var(--dur-fast) var(--ease-out)' }}><Icon.ChevronRight size={16}/></span>
        {rb && <DBadge tone={rb.tone} size="sm">{rb.label}</DBadge>}
        <span style={{ flex: 1, minWidth: 0, font: 'var(--body)', fontWeight: 600, color: 'var(--text-strong)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
          {cert.subject.cn || cert.subject.str || '(no common name)'}
        </span>
        {!isCsr && <StatusPill status={cert.status} size="sm" />}
        {!isCsr && cert.notAfter && <span style={{ font: 'var(--body-sm)', color: 'var(--text-muted)', whiteSpace: 'nowrap' }}>exp {fmtDate(cert.notAfter)}</span>}
      </div>
      {open && <div style={{ marginTop: 'var(--space-4)' }}><CertDetails cert={cert} /></div>}
    </DCard>
  );
}

// chain-health verdict banner: correct & optimal / improvable / wrong
function ChainHealth({ report }) {
  if (!report) return null;
  const tone = report.level === 'error' ? 'danger' : report.level === 'warning' ? 'warning' : report.level === 'pending' ? 'info' : 'success';
  const icon = report.level === 'pending' ? <Icon.Clock size={18}/> : report.level === 'ok' ? <Icon.Check size={18}/> : <Icon.Alert size={18}/>;
  return (
    <Notice tone={tone} title={report.headline} icon={icon}>
      <ul style={{ margin: '4px 0 0', paddingLeft: 18, display: 'flex', flexDirection: 'column', gap: 4 }}>
        {report.items.map((it, i) => (
          <li key={i} style={{ color: it.level === 'error' ? 'var(--danger-fg)' : it.level === 'warning' ? 'var(--amber-700)' : 'var(--text-body)' }}>{it.text}</li>
        ))}
      </ul>
    </Notice>
  );
}

// Decoded output view — fed by the shared input in the parent toolkit.
function DecodedView({ results, error, health }) {
  if (error) {
    return <Notice tone="danger" title="Could not decode">{error}</Notice>;
  }
  if (!results) {
    return (
      <DCard 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.Shield size={36}/></div>
          <div style={{ font: 'var(--body)', color: 'var(--text-body)' }}>Paste, upload, or fetch a certificate to see every field decoded.</div>
          <div style={{ font: 'var(--body-sm)', marginTop: 6 }}>Or press <strong>Load sample</strong> to generate one.</div>
        </div>
      </DCard>
    );
  }
  const roles = rolesForResults(results);
  const multi = results.length > 1;
  return (
    <div style={{ minWidth: 0, display: 'flex', flexDirection: 'column', gap: 'var(--space-5)' }}>
      {health && <ChainHealth report={health} />}
      <div style={{ display: 'flex', flexDirection: 'column', gap: multi ? 'var(--space-3)' : 'var(--space-8)' }}>
        {results.map((c, i) => multi
          ? <CollapsibleCert key={i} cert={c} role={roles[i]} defaultOpen={roles[i] === 'leaf' || roles[i] === 'csr'} />
          : <CertResult key={i} cert={c} role={roles[i]} />)}
      </div>
    </div>
  );
}

window.ToolkitInput = ToolkitInput;
window.DecodedView = DecodedView;
window.CertResult = CertResult;
