/* global React, Card, Button, Input, Ic, Breadcrumb, VS, LoadingState, ErrorState, SchemaPicker, NormPicker */

// ============================================================
// LIST SCREEN
// ============================================================
function OneTimeProtocolsScreen() {
  const [activeId, setActiveId] = React.useState(null);
  const [list, setList] = React.useState(null);
  const [err, setErr] = React.useState('');
  const [busy, setBusy] = React.useState(false);

  const load = React.useCallback(() => {
    setList(null); setErr('');
    VS.oneTimeProtocols()
      .then((r) => setList(r))
      .catch((e) => setErr(e.message));
  }, []);

  React.useEffect(() => { load(); }, [load]);

  async function createNew() {
    setBusy(true);
    try {
      const r = await VS.createOneTimeProtocol();
      setActiveId(r.id);
    } catch (e) {
      setErr(e.message);
    } finally {
      setBusy(false);
    }
  }

  async function removeRow(id) {
    if (!confirm('Usunąć ten protokół?')) return;
    try {
      await VS.deleteOneTimeProtocol(id);
      load();
    } catch (e) {
      alert('Błąd: ' + e.message);
    }
  }

  if (activeId) {
    return (
      <OneTimeProtocolEditor
        protocolId={activeId}
        onBack={() => { setActiveId(null); load(); }}
      />
    );
  }

  return (
    <div style={{ padding: 24, display: 'flex', flexDirection: 'column', gap: 16 }}>
      <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between' }}>
        <div>
          <div className="uppercase-label" style={{ marginBottom: 6 }}>Raporty</div>
          <h1 style={{ margin: 0, fontSize: 24, fontWeight: 500, letterSpacing: '-0.02em' }}>Protokoły jednorazowe</h1>
          <div style={{ fontSize: 12.5, color: 'var(--fg-muted)', marginTop: 6 }}>
            Pomiary wibracji realizowane poza cyklem harmonogramowym.
          </div>
        </div>
        <Button variant="primary" icon={<Ic.plus size={12} />} onClick={createNew} disabled={busy}>
          {busy ? 'Tworzę…' : 'Nowy protokół'}
        </Button>
      </div>

      {err && <ErrorState message={err} />}
      {!err && !list && <LoadingState />}
      {!err && list && list.length === 0 && (
        <Card>
          <div style={{ textAlign: 'center', padding: 28, color: 'var(--fg-muted)', fontSize: 13 }}>
            <div className="uppercase-label" style={{ marginBottom: 6 }}>Brak protokołów</div>
            <div>Utwórz nowy protokół aby zacząć.</div>
          </div>
        </Card>
      )}
      {!err && list && list.length > 0 && (
        <Card pad={false}>
          <div style={{
            display: 'grid', gridTemplateColumns: '1fr 1fr 110px 130px 140px 200px',
            gap: 10, padding: '10px 16px', borderBottom: '1px solid var(--hairline)',
            fontSize: 10, color: 'var(--fg-muted)', textTransform: 'uppercase', letterSpacing: '0.06em', fontWeight: 600,
          }}>
            <div>Zakład</div>
            <div>Maszyna</div>
            <div>Data</div>
            <div>Ost. edycja</div>
            <div>Autor</div>
            <div>Akcje</div>
          </div>
          {list.map((p, i) => (
            <div
              key={p.id}
              style={{
                display: 'grid', gridTemplateColumns: '1fr 1fr 110px 130px 140px 200px',
                gap: 10, padding: '10px 16px',
                borderBottom: i < list.length - 1 ? '1px solid var(--hairline)' : 'none',
                alignItems: 'center', fontSize: 12.5, cursor: 'pointer',
              }}
              onClick={() => setActiveId(p.id)}
              onMouseEnter={(e) => e.currentTarget.style.background = 'var(--bg-sunken)'}
              onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
            >
              <div style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{p.site_name || '—'}</div>
              <div style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{p.machine_name || '—'}</div>
              <div className="mono" style={{ color: 'var(--fg-muted)' }}>{p.data || '—'}</div>
              <div className="mono" style={{ color: 'var(--fg-muted)', fontSize: 11 }}>
                {p.updated_at ? p.updated_at.slice(0, 16).replace('T', ' ') : '—'}
              </div>
              <div className="mono" style={{ fontSize: 11, color: 'var(--fg-muted)' }}>{p.created_by || '—'}</div>
              <div style={{ display: 'flex', gap: 6 }} onClick={(e) => e.stopPropagation()}>
                <Button size="sm" variant="ghost" icon={<Ic.edit size={11} />} onClick={() => setActiveId(p.id)}>
                  Edytuj
                </Button>
                <Button size="sm" variant="ghost" icon={<Ic.download size={11} />}
                  onClick={() => VS.openOneTimePdf(p.id).catch(e => alert('Błąd pobierania PDF: ' + e.message))}>
                  PDF
                </Button>
                <Button size="sm" variant="danger" onClick={() => removeRow(p.id)}>
                  Usuń
                </Button>
              </div>
            </div>
          ))}
        </Card>
      )}
    </div>
  );
}

// ============================================================
// EDITOR
// ============================================================
const CEL_POMIARU_OPTS = [
  'Kontrolny', 'Odbiorczy', 'Powykonawczy', 'Diagnostyczny', 'Po remoncie', 'Po wyosiowaniu',
];
const SPRZET_OPTS = [
  'SKF Microlog Analyzer dBX',
  'SKF Microlog Analyzer CMXA80.',
  'SKF Microlog Analyzer CMVA60.',
];
const STAN_DYN_OPTS = [
  'Stan dynamiczny zgodnie z normą (ISO 10816-3) dopuszczalny.',
  'Stan dynamiczny dopuszczalny — praca bez ograniczeń czasowych.',
  'Stan dynamiczny wymaga obserwacji.',
  'Stan dynamiczny niedopuszczalny — konieczna interwencja.',
];
const ZALECENIA_OPTS = [
  'Brak zaleceń.',
  'Zalecany kontrolny pomiar drgań za 3 miesiące.',
  'Zalecana wymiana łożysk.',
  'Zalecane osiowanie zespołu.',
  'Zalecane wyważenie dynamiczne.',
];
const UWAGI_OPTS = [
  'Bez uwag.',
  'Wartości pozostawione po wyosiowaniu zespołu.',
  'Pomiar po remoncie.',
  'Maszyna pracowała w warunkach nominalnych.',
];

function emptyBearings() {
  return [1, 2, 3, 4].map((nr) => ({
    nr, v_pion: null, env: null, acc: null, hfd: null, v_poz: null, v_osiowy: null,
  }));
}

function OneTimeProtocolEditor({ protocolId, onBack }) {
  const [protocol, setProtocol] = React.useState(null);
  const [err, setErr] = React.useState('');
  const [sites, setSites] = React.useState([]);
  const [machines, setMachines] = React.useState([]);
  const [users, setUsers] = React.useState(null); // null = nieznane, [] = brak
  const [dirty, setDirty] = React.useState(false);
  const [busy, setBusy] = React.useState(false);
  const [msg, setMsg] = React.useState('');
  const [schemaBust, setSchemaBust] = React.useState(0);
  const [showPicker, setShowPicker] = React.useState(false);
  const [showNormPicker, setShowNormPicker] = React.useState(false);
  const [normsList, setNormsList] = React.useState(null);
  const schemaFileRef = React.useRef(null);

  React.useEffect(() => {
    VS.oneTimeProtocol(protocolId)
      .then((p) => {
        // Ensure bearings array exists
        const bearings = Array.isArray(p.bearings) && p.bearings.length > 0 ? p.bearings : emptyBearings();
        const osiowanie = p.osiowanie || { rozw_poz: null, prz_poz: null, rozw_pion: null, prz_pion: null };
        setProtocol({ ...p, bearings, osiowanie });
      })
      .catch((e) => setErr(e.message));
    VS.sites().then((s) => setSites(s || [])).catch(() => {});
    // Try user-stamps (admin). Fallback: current user only.
    VS.userStamps()
      .then((list) => setUsers(list || []))
      .catch(() => {
        // fallback — current user only
        VS.myStamp()
          .then((me) => {
            setUsers([{
              user_id: me.user_id, username: me.username, role: 'user',
              name: me.name, cert_number: me.cert_number, has_image: me.has_image,
            }]);
          })
          .catch(() => setUsers([]));
      });
  }, [protocolId]);

  React.useEffect(() => {
    VS.norms().then((r) => setNormsList(r || [])).catch(() => setNormsList([]));
  }, []);

  // Load machines when site_id changes
  React.useEffect(() => {
    if (!protocol) return;
    if (protocol.site_id) {
      VS.siteMachines(protocol.site_id)
        .then((m) => setMachines(m || []))
        .catch(() => setMachines([]));
    } else {
      setMachines([]);
    }
  }, [protocol && protocol.site_id]);

  function patch(p) {
    setProtocol((prev) => ({ ...prev, ...p }));
    setDirty(true);
  }

  function patchOsiowanie(p) {
    setProtocol((prev) => ({ ...prev, osiowanie: { ...prev.osiowanie, ...p } }));
    setDirty(true);
  }

  function updateBearing(idx, field, value) {
    setProtocol((prev) => {
      const bearings = prev.bearings.map((b, i) => (i === idx ? { ...b, [field]: value } : b));
      return { ...prev, bearings };
    });
    setDirty(true);
  }

  function addBearing() {
    setProtocol((prev) => {
      const maxNr = prev.bearings.reduce((a, b) => Math.max(a, b.nr || 0), 0);
      const bearings = [...prev.bearings, {
        nr: maxNr + 1 || 1,
        v_pion: null, env: null, acc: null, hfd: null, v_poz: null, v_osiowy: null,
      }];
      return { ...prev, bearings };
    });
    setDirty(true);
  }

  function removeBearing(idx) {
    setProtocol((prev) => ({ ...prev, bearings: prev.bearings.filter((_, i) => i !== idx) }));
    setDirty(true);
  }

  async function save() {
    if (!protocol) return;
    setBusy(true); setMsg('');
    try {
      const payload = {
        site_id: protocol.site_id,
        site_name: protocol.site_name || '',
        machine_id: protocol.machine_id,
        machine_name: protocol.machine_name || '',
        data: protocol.data || null,
        cel_pomiaru: protocol.cel_pomiaru || '',
        sprzet: protocol.sprzet || '',
        opis: protocol.opis || '',
        stan_dynamiczny: protocol.stan_dynamiczny || '',
        zalecenia: protocol.zalecenia || '',
        uwagi: protocol.uwagi || '',
        osiowanie: protocol.osiowanie || {},
        bearings: protocol.bearings || [],
        opracowal_user_id: protocol.opracowal_user_id,
        zatwierdzil_user_id: protocol.zatwierdzil_user_id,
        schema_library_id: protocol.schema_library_id ?? null,
        norm_id: protocol.norm_id ?? null,
      };
      await VS.saveOneTimeProtocol(protocolId, payload);
      setDirty(false);
      setMsg('Zapisano.');
      setTimeout(() => setMsg(''), 2500);
    } catch (e) {
      setMsg('Błąd: ' + e.message);
    } finally {
      setBusy(false);
    }
  }

  async function uploadSchema(e) {
    const file = e.target.files && e.target.files[0];
    if (!file) return;
    setBusy(true); setMsg('');
    try {
      await VS.uploadOneTimeSchemaImage(protocolId, file);
      // Wgrany custom wyłącza wybór z biblioteki — backend woli custom, ale
      // dla spójności zerujemy też schema_library_id i zapisujemy.
      setProtocol((prev) => ({ ...prev, has_schema_image: true, schema_library_id: null }));
      if (protocol && protocol.schema_library_id) {
        try {
          await VS.saveOneTimeProtocol(protocolId, { schema_library_id: null });
        } catch (_) { /* best-effort */ }
      }
      setDirty(false);
      setSchemaBust((x) => x + 1);
      setMsg('Schemat wgrany.');
      setTimeout(() => setMsg(''), 2500);
    } catch (err) {
      setMsg('Błąd: ' + err.message);
    } finally {
      setBusy(false);
      e.target.value = '';
    }
  }

  async function pickFromLibrary(id, name) {
    setProtocol((prev) => ({ ...prev, schema_library_id: id, schema_library_name: name }));
    setDirty(true);
  }

  async function clearSchema() {
    setProtocol((prev) => ({ ...prev, schema_library_id: null }));
    setDirty(true);
  }

  async function removeProtocol() {
    if (!confirm('Usunąć ten protokół? Operacja nieodwracalna.')) return;
    try {
      await VS.deleteOneTimeProtocol(protocolId);
      onBack();
    } catch (e) {
      alert('Błąd: ' + e.message);
    }
  }

  async function downloadPdf() {
    // zapisz najpierw żeby PDF był aktualny
    if (dirty) {
      await save();
    }
    try {
      await VS.openOneTimePdf(protocolId);
    } catch (e) {
      alert('Błąd pobierania PDF: ' + e.message);
    }
  }

  if (err) return <ErrorState message={err} />;
  if (!protocol) return <LoadingState />;

  const siteOptions = (sites || []).map((s) => ({ id: s.id, label: s.name }));
  const machineOptions = (machines || []).map((m) => ({ id: m.id, label: m.name }));

  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: 'calc(100vh - 56px)', overflow: 'hidden' }}>
      <div style={{ padding: '18px 24px', borderBottom: '1px solid var(--border)', flexShrink: 0 }}>
        <Breadcrumb items={[
          { label: 'Protokoły jednorazowe', onClick: onBack },
          { label: `#${protocolId}${protocol.machine_name ? ' · ' + protocol.machine_name : ''}` },
        ]} />
        <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', marginTop: 12 }}>
          <div>
            <h1 style={{ margin: 0, fontSize: 22, fontWeight: 500, letterSpacing: '-0.02em' }}>
              Protokół jednorazowy <span className="mono" style={{ color: 'var(--fg-muted)' }}>#{protocolId}</span>
            </h1>
            <div style={{ fontSize: 11.5, color: 'var(--fg-muted)', marginTop: 6 }}>
              {dirty ? (
                <span style={{ color: 'var(--warn)' }}>● Niezapisane zmiany</span>
              ) : (
                <span>Wszystkie zmiany zapisane</span>
              )}
              {msg && (
                <span className="mono" style={{ marginLeft: 12, color: msg.startsWith('Błąd') ? 'var(--alarm)' : 'var(--ok)' }}>
                  {msg}
                </span>
              )}
            </div>
          </div>
        </div>
      </div>

      <div style={{ flex: 1, overflowY: 'auto', padding: 24, display: 'flex', flexDirection: 'column', gap: 16 }}>
        {/* 1. Metadane */}
        <Card>
          <div className="uppercase-label" style={{ marginBottom: 14 }}>Metadane</div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 12 }}>
            <FieldLabel label="Zakład">
              <AutocompleteInput
                value={protocol.site_name || ''}
                selectedId={protocol.site_id}
                options={siteOptions}
                placeholder="Wybierz lub wpisz zakład…"
                onChange={(val, id) => patch({ site_name: val, site_id: id, machine_id: null, machine_name: protocol.machine_name })}
              />
            </FieldLabel>

            <FieldLabel label="Maszyna">
              <AutocompleteInput
                value={protocol.machine_name || ''}
                selectedId={protocol.machine_id}
                options={machineOptions}
                placeholder={protocol.site_id ? 'Wybierz lub wpisz maszynę…' : 'Wpisz maszynę…'}
                onChange={(val, id) => patch({ machine_name: val, machine_id: id })}
              />
            </FieldLabel>

            <FieldLabel label="Data">
              <input
                type="date"
                value={protocol.data || ''}
                onChange={(e) => patch({ data: e.target.value })}
                style={inputStyle}
              />
            </FieldLabel>

            <FieldLabel label="Cel pomiaru">
              <DatalistInput
                id={`celp-${protocolId}`}
                options={CEL_POMIARU_OPTS}
                value={protocol.cel_pomiaru || ''}
                onChange={(v) => patch({ cel_pomiaru: v })}
                placeholder="np. Kontrolny"
              />
            </FieldLabel>

            <FieldLabel label="Sprzęt pomiarowy" span={2}>
              <TextareaCombobox
                options={SPRZET_OPTS}
                value={protocol.sprzet || ''}
                onChange={(v) => patch({ sprzet: v })}
                placeholder="np. SKF Microlog Analyzer CMXA80"
                rows={2}
              />
            </FieldLabel>
          </div>
        </Card>

        {/* 2. Opis urządzenia */}
        <Card>
          <div className="uppercase-label" style={{ marginBottom: 10 }}>Opis urządzenia</div>
          <textarea
            rows={3}
            value={protocol.opis || ''}
            onChange={(e) => patch({ opis: e.target.value })}
            placeholder="Opis urządzenia, warunki pomiaru, uwagi wstępne…"
            style={textareaStyle}
          />
        </Card>

        {/* 3. Schemat kinematyczny */}
        <Card>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
            <div className="uppercase-label">Schemat kinematyczny</div>
            <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
              <input
                ref={schemaFileRef}
                type="file"
                accept="image/png,image/jpeg"
                onChange={uploadSchema}
                style={{ display: 'none' }}
              />
              <Button size="sm" variant="primary" icon={<Ic.search size={12} />} onClick={() => setShowPicker(true)} disabled={busy}>
                Wybierz z biblioteki
              </Button>
              <Button size="sm" icon={<Ic.upload size={12} />} onClick={() => schemaFileRef.current && schemaFileRef.current.click()} disabled={busy}>
                Wgraj własny
              </Button>
              {(protocol.schema_library_id || protocol.has_schema_image) && (
                <Button size="sm" variant="ghost" onClick={clearSchema} disabled={busy}>
                  Usuń wybór
                </Button>
              )}
            </div>
          </div>
          {protocol.schema_library_id && (
            <div className="mono" style={{
              fontSize: 10.5, color: 'var(--fg-muted)',
              marginBottom: 8, letterSpacing: '0.04em',
            }}>
              Biblioteka · ID {protocol.schema_library_id}
              {protocol.schema_library_name ? ` · ${protocol.schema_library_name}` : ''}
            </div>
          )}
          <div style={{
            width: '100%', minHeight: 120, maxHeight: 240,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            border: '1px dashed var(--border)',
            borderRadius: 'var(--r-sm)',
            background: 'var(--bg-sunken)',
            padding: 8,
          }}>
            {protocol.schema_library_id ? (
              <img
                src={VS.schemaLibraryImageUrl(protocol.schema_library_id)}
                alt="schemat z biblioteki"
                style={{ maxWidth: '100%', maxHeight: 220, objectFit: 'contain' }}
              />
            ) : protocol.has_schema_image ? (
              <img
                src={VS.oneTimeSchemaImageUrl(protocolId, schemaBust)}
                alt="schemat kinematyczny"
                style={{ maxWidth: '100%', maxHeight: 220, objectFit: 'contain' }}
              />
            ) : (
              <span className="uppercase-label" style={{ color: 'var(--fg-subtle)' }}>Nie wybrano schematu</span>
            )}
          </div>
          {showPicker && (
            <SchemaPicker
              currentId={protocol.schema_library_id}
              onPick={pickFromLibrary}
              onClose={() => setShowPicker(false)}
            />
          )}
        </Card>

        {/* 3b. Norma */}
        <Card>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
            <div className="uppercase-label">Norma</div>
            <div style={{ display: 'flex', gap: 8 }}>
              <Button size="sm" variant="primary" icon={<Ic.search size={12} />} onClick={() => setShowNormPicker(true)}>
                {protocol.norm_id ? 'Zmień normę' : 'Wybierz normę'}
              </Button>
              {protocol.norm_id && (
                <Button size="sm" variant="ghost" onClick={() => patch({ norm_id: null })}>
                  Usuń wybór
                </Button>
              )}
            </div>
          </div>
          {(() => {
            const current = (normsList || []).find((n) => n.id === protocol.norm_id);
            if (!protocol.norm_id || !current) {
              return (
                <div style={{
                  padding: 14, background: 'var(--bg-sunken)',
                  border: '1px dashed var(--border)', borderRadius: 'var(--r-sm)',
                  fontSize: 12, color: 'var(--fg-muted)',
                }}>
                  Jeżeli nie wybierzesz normy, w PDF zostanie użyta domyślna (ISO 10816-3 — posadowienie sztywne).
                </div>
              );
            }
            return (
              <div style={{
                padding: 14, background: 'var(--bg-sunken)',
                border: '1px solid var(--border)', borderRadius: 'var(--r-sm)',
                display: 'flex', flexDirection: 'column', gap: 8,
              }}>
                <div style={{ fontSize: 14, fontWeight: 500, color: 'var(--fg)' }}>{current.numer || '—'}</div>
                <div style={{ fontSize: 12, fontFamily: 'var(--font-mono)', color: 'var(--fg-muted)' }}>{current.wartosci || ''}</div>
                <div style={{ fontSize: 12, color: 'var(--fg)', lineHeight: 1.5, marginTop: 4 }}>
                  {current.opis || ''}
                </div>
              </div>
            );
          })()}
          {showNormPicker && (
            <NormPicker
              currentId={protocol.norm_id}
              onPick={(id) => { patch({ norm_id: id }); }}
              onClose={() => setShowNormPicker(false)}
            />
          )}
        </Card>

        {/* 4. Tabela pomiarów */}
        <Card pad={false}>
          <div style={{ padding: '12px 16px', borderBottom: '1px solid var(--hairline)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
            <div className="uppercase-label">Tabela pomiarów</div>
            <span className="mono" style={{ fontSize: 10, color: 'var(--fg-subtle)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>
              {protocol.bearings.length} łożysk
            </span>
          </div>
          <div style={{ overflowX: 'auto' }}>
            <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 12 }}>
              <thead>
                <tr style={{ background: 'var(--bg-sunken)' }}>
                  <th style={thStyle}>Nr łoż.</th>
                  <th style={thStyle}>V pion [mm/s]</th>
                  <th style={thStyle}>ENV [g/E]</th>
                  <th style={thStyle}>ACC [g]</th>
                  <th style={thStyle}>HFD</th>
                  <th style={thStyle}>V poz. [mm/s]</th>
                  <th style={thStyle}>V osiowy [mm/s]</th>
                  <th style={{ ...thStyle, width: 30 }}></th>
                </tr>
              </thead>
              <tbody>
                {protocol.bearings.map((b, i) => (
                  <tr key={i} style={{ borderTop: '1px solid var(--hairline)' }}>
                    <td style={tdStyle}>
                      <input
                        type="number"
                        value={b.nr ?? ''}
                        onChange={(e) => updateBearing(i, 'nr', e.target.value === '' ? null : parseInt(e.target.value, 10))}
                        style={{ ...cellInputStyle, textAlign: 'center', fontWeight: 600 }}
                      />
                    </td>
                    <NumCell value={b.v_pion} onChange={(v) => updateBearing(i, 'v_pion', v)} />
                    <NumCell value={b.env} onChange={(v) => updateBearing(i, 'env', v)} />
                    <NumCell value={b.acc} onChange={(v) => updateBearing(i, 'acc', v)} />
                    <NumCell value={b.hfd} onChange={(v) => updateBearing(i, 'hfd', v)} />
                    <NumCell value={b.v_poz} onChange={(v) => updateBearing(i, 'v_poz', v)} />
                    <NumCell value={b.v_osiowy} onChange={(v) => updateBearing(i, 'v_osiowy', v)} />
                    <td style={{ ...tdStyle, textAlign: 'center' }}>
                      <button
                        onClick={() => removeBearing(i)}
                        title="Usuń wiersz"
                        style={{
                          background: 'transparent', border: 'none',
                          color: 'var(--fg-subtle)', cursor: 'pointer', padding: 4,
                        }}
                      >×</button>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
          <div style={{ padding: 12, borderTop: '1px solid var(--hairline)' }}>
            <Button size="sm" icon={<Ic.plus size={12} />} onClick={addBearing}>
              Dodaj łożysko
            </Button>
          </div>
        </Card>

        {/* 5. Osiowanie (rozwijany) */}
        <OsiowaniePanel osiowanie={protocol.osiowanie} onChange={patchOsiowanie} />

        {/* 6. Stan dynamiczny / Zalecenia / Uwagi */}
        <Card>
          <div className="uppercase-label" style={{ marginBottom: 14 }}>Ocena i wnioski</div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 12 }}>
            <FieldLabel label="Stan dynamiczny">
              <TextareaCombobox
                options={STAN_DYN_OPTS}
                value={protocol.stan_dynamiczny || ''}
                onChange={(v) => patch({ stan_dynamiczny: v })}
                placeholder="Wybierz lub wpisz…"
                rows={3}
              />
            </FieldLabel>
            <FieldLabel label="Zalecenia">
              <TextareaCombobox
                options={ZALECENIA_OPTS}
                value={protocol.zalecenia || ''}
                onChange={(v) => patch({ zalecenia: v })}
                placeholder="Wybierz lub wpisz…"
                rows={3}
              />
            </FieldLabel>
            <FieldLabel label="Uwagi">
              <TextareaCombobox
                options={UWAGI_OPTS}
                value={protocol.uwagi || ''}
                onChange={(v) => patch({ uwagi: v })}
                placeholder="Wybierz lub wpisz…"
                rows={3}
              />
            </FieldLabel>
          </div>
        </Card>

        {/* 7. Podpisy */}
        <Card>
          <div className="uppercase-label" style={{ marginBottom: 14 }}>Podpisy</div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 16 }}>
            <SignatureBlock
              label="Opracował"
              users={users}
              userId={protocol.opracowal_user_id}
              onChange={(id) => patch({ opracowal_user_id: id })}
            />
            <SignatureBlock
              label="Zatwierdził"
              users={users}
              userId={protocol.zatwierdzil_user_id}
              onChange={(id) => patch({ zatwierdzil_user_id: id })}
            />
          </div>
        </Card>
      </div>

      {/* Sticky bottom bar */}
      <div style={{
        position: 'sticky', bottom: 0,
        background: 'var(--bg-elev)',
        borderTop: '1px solid var(--border)',
        padding: '12px 24px',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        gap: 10, flexShrink: 0,
      }}>
        <Button variant="ghost" icon={<Ic.chevronLeft size={12} />} onClick={onBack}>
          Wróć do listy
        </Button>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          {dirty && (
            <span className="mono" style={{ fontSize: 11, color: 'var(--warn)' }}>● niezapisane zmiany</span>
          )}
          {msg && (
            <span className="mono" style={{ fontSize: 11, color: msg.startsWith('Błąd') ? 'var(--alarm)' : 'var(--ok)' }}>
              {msg}
            </span>
          )}
          <Button variant="primary" onClick={save} disabled={busy}>
            {busy ? 'Zapisuję…' : 'Zapisz'}
          </Button>
          <Button icon={<Ic.download size={12} />} onClick={downloadPdf} disabled={busy}>
            Pobierz PDF
          </Button>
          <Button variant="danger" onClick={removeProtocol}>
            Usuń protokół
          </Button>
        </div>
      </div>
    </div>
  );
}

// ============================================================
// STYLES
// ============================================================
const inputStyle = {
  width: '100%', height: 30, padding: '0 10px',
  background: 'var(--surface)',
  border: '1px solid var(--border-strong)',
  borderRadius: 'var(--r-sm)',
  fontSize: 12, color: 'var(--fg)', outline: 'none',
  fontFamily: 'inherit',
};
const textareaStyle = {
  width: '100%', padding: 10,
  background: 'var(--surface)',
  border: '1px solid var(--border-strong)',
  borderRadius: 'var(--r-sm)',
  fontSize: 12, color: 'var(--fg)', outline: 'none',
  fontFamily: 'inherit', resize: 'vertical',
};
const thStyle = {
  padding: '8px 10px',
  fontSize: 10,
  fontWeight: 600,
  textAlign: 'left',
  textTransform: 'uppercase',
  letterSpacing: '0.04em',
  color: 'var(--fg-muted)',
  borderBottom: '1px solid var(--hairline)',
  whiteSpace: 'nowrap',
};
const tdStyle = {
  padding: '4px 6px',
};
const cellInputStyle = {
  width: '100%', height: 26, padding: '0 6px',
  background: 'var(--surface)',
  border: '1px solid var(--border-strong)',
  borderRadius: 'var(--r-sm)',
  fontSize: 12, color: 'var(--fg)', outline: 'none',
  fontFamily: 'var(--font-mono)',
};

// ============================================================
// SUBCOMPONENTS
// ============================================================
function FieldLabel({ label, children, span }) {
  return (
    <label style={{ gridColumn: span ? `span ${span}` : undefined }}>
      <div className="uppercase-label" style={{ marginBottom: 6 }}>{label}</div>
      {children}
    </label>
  );
}

function NumCell({ value, onChange }) {
  return (
    <td style={tdStyle}>
      <input
        type="number"
        step="any"
        value={value ?? ''}
        onChange={(e) => {
          const v = e.target.value;
          onChange(v === '' ? null : parseFloat(v));
        }}
        style={cellInputStyle}
      />
    </td>
  );
}

// Autocomplete: input + filtered dropdown, selecting options sets id; free text sets id=null
function AutocompleteInput({ value, selectedId, options, placeholder, onChange }) {
  const [open, setOpen] = React.useState(false);
  const wrapRef = React.useRef(null);

  React.useEffect(() => {
    function onDoc(e) {
      if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
    }
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, []);

  const q = (value || '').toLowerCase();
  const filtered = (options || []).filter((o) => !q || (o.label || '').toLowerCase().includes(q));

  function selectOption(o) {
    onChange(o.label, o.id);
    setOpen(false);
  }

  return (
    <div ref={wrapRef} style={{ position: 'relative' }}>
      <input
        value={value || ''}
        onChange={(e) => { onChange(e.target.value, null); setOpen(true); }}
        onFocus={() => setOpen(true)}
        placeholder={placeholder}
        style={inputStyle}
      />
      {selectedId != null && (
        <div className="mono" style={{
          position: 'absolute', right: 8, top: 8,
          fontSize: 9.5, color: 'var(--ok)',
          background: 'var(--ok-weak)',
          padding: '1px 5px', borderRadius: 3,
          border: '1px solid var(--ok-border)',
          pointerEvents: 'none',
        }}>
          ID {selectedId}
        </div>
      )}
      {open && filtered.length > 0 && (
        <div style={{
          position: 'absolute', top: 'calc(100% + 2px)', left: 0, right: 0,
          maxHeight: 200, overflowY: 'auto',
          background: 'var(--surface)',
          border: '1px solid var(--border-strong)',
          borderRadius: 'var(--r-sm)',
          boxShadow: '0 6px 20px rgba(0,0,0,0.12)',
          zIndex: 20,
        }}>
          {filtered.map((o) => (
            <div
              key={o.id}
              onClick={() => selectOption(o)}
              style={{
                padding: '7px 10px', fontSize: 12.5, cursor: 'pointer',
                borderBottom: '1px solid var(--hairline)',
              }}
              onMouseEnter={(e) => e.currentTarget.style.background = 'var(--bg-sunken)'}
              onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
            >
              {o.label}
              <span className="mono" style={{ fontSize: 10, color: 'var(--fg-subtle)', marginLeft: 8 }}>#{o.id}</span>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// Single-line combobox using native <datalist>
function DatalistInput({ id, options, value, onChange, placeholder }) {
  return (
    <>
      <input
        list={id}
        value={value || ''}
        onChange={(e) => onChange(e.target.value)}
        placeholder={placeholder}
        style={inputStyle}
      />
      <datalist id={id}>
        {options.map((o, i) => <option key={i} value={o} />)}
      </datalist>
    </>
  );
}

// Multi-line combobox: textarea + dropdown of options (click = fill)
function TextareaCombobox({ options, value, onChange, placeholder, rows = 2 }) {
  const [open, setOpen] = React.useState(false);
  const wrapRef = React.useRef(null);

  React.useEffect(() => {
    function onDoc(e) {
      if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
    }
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, []);

  return (
    <div ref={wrapRef} style={{ position: 'relative' }}>
      <div style={{ position: 'relative' }}>
        <textarea
          rows={rows}
          value={value || ''}
          onChange={(e) => onChange(e.target.value)}
          placeholder={placeholder}
          style={{ ...textareaStyle, paddingRight: 28 }}
        />
        <button
          onClick={() => setOpen((o) => !o)}
          title="Wybierz z listy"
          style={{
            position: 'absolute', right: 6, top: 6,
            width: 22, height: 22,
            background: 'transparent',
            border: '1px solid var(--border)',
            borderRadius: 3,
            cursor: 'pointer',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            color: 'var(--fg-muted)',
          }}
        >
          <Ic.chevronDown size={12} />
        </button>
      </div>
      {open && (
        <div style={{
          position: 'absolute', top: 'calc(100% + 2px)', left: 0, right: 0,
          maxHeight: 260, overflowY: 'auto',
          background: 'var(--surface)',
          border: '1px solid var(--border-strong)',
          borderRadius: 'var(--r-sm)',
          boxShadow: '0 6px 20px rgba(0,0,0,0.12)',
          zIndex: 20,
        }}>
          {options.map((o, i) => (
            <div
              key={i}
              onClick={() => { onChange(o); setOpen(false); }}
              style={{
                padding: '8px 10px', fontSize: 12, cursor: 'pointer',
                borderBottom: '1px solid var(--hairline)',
                lineHeight: 1.4,
              }}
              onMouseEnter={(e) => e.currentTarget.style.background = 'var(--bg-sunken)'}
              onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
            >
              {o}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

function OsiowaniePanel({ osiowanie, onChange }) {
  const [open, setOpen] = React.useState(false);
  const o = osiowanie || {};
  return (
    <Card>
      <div
        onClick={() => setOpen((x) => !x)}
        style={{
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          cursor: 'pointer', userSelect: 'none',
        }}
      >
        <div className="uppercase-label">Osiowanie</div>
        <div style={{ color: 'var(--fg-muted)', transform: open ? 'rotate(180deg)' : 'rotate(0)', transition: 'transform 0.15s' }}>
          <Ic.chevronDown size={16} />
        </div>
      </div>
      {open && (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 12, marginTop: 14 }}>
          <OsiowanieField label="Rozwarcie — płaszczyzna pozioma [mm]"
            value={o.rozw_poz} onChange={(v) => onChange({ rozw_poz: v })} />
          <OsiowanieField label="Przesunięcie — pł. pozioma [mm]"
            value={o.prz_poz} onChange={(v) => onChange({ prz_poz: v })} />
          <OsiowanieField label="Rozwarcie — pł. pionowa [mm]"
            value={o.rozw_pion} onChange={(v) => onChange({ rozw_pion: v })} />
          <OsiowanieField label="Przesunięcie — pł. pionowa [mm]"
            value={o.prz_pion} onChange={(v) => onChange({ prz_pion: v })} />
        </div>
      )}
    </Card>
  );
}

function OsiowanieField({ label, value, onChange }) {
  return (
    <label>
      <div className="uppercase-label" style={{ marginBottom: 6 }}>{label}</div>
      <input
        type="number"
        step="any"
        value={value ?? ''}
        onChange={(e) => onChange(e.target.value === '' ? null : parseFloat(e.target.value))}
        style={{ ...inputStyle, fontFamily: 'var(--font-mono)' }}
      />
    </label>
  );
}

function SignatureBlock({ label, users, userId, onChange }) {
  const selected = users ? users.find((u) => u.user_id === userId) : null;
  const isLoading = users === null;

  return (
    <div style={{
      padding: 14, border: '1px solid var(--hairline)',
      borderRadius: 'var(--r-sm)', background: 'var(--bg-sunken)',
    }}>
      <div className="uppercase-label" style={{ marginBottom: 8 }}>{label}</div>
      {isLoading ? (
        <div style={{ fontSize: 12, color: 'var(--fg-muted)' }}>Ładowanie…</div>
      ) : users.length === 0 ? (
        <div style={{ fontSize: 12, color: 'var(--fg-muted)' }}>Brak dostępnych użytkowników.</div>
      ) : (
        <select
          value={userId ?? ''}
          onChange={(e) => onChange(e.target.value === '' ? null : parseInt(e.target.value, 10))}
          style={{
            width: '100%', height: 30, padding: '0 10px',
            background: 'var(--surface)',
            border: '1px solid var(--border-strong)',
            borderRadius: 'var(--r-sm)',
            fontSize: 12, color: 'var(--fg)', outline: 'none',
            fontFamily: 'inherit',
          }}
        >
          <option value="">— nie wybrano —</option>
          {users.map((u) => (
            <option key={u.user_id} value={u.user_id}>
              {u.name ? `${u.name} (${u.username})` : u.username}
            </option>
          ))}
        </select>
      )}

      {selected && (
        <div style={{ marginTop: 10, display: 'flex', gap: 12, alignItems: 'flex-start' }}>
          <div style={{
            width: 120, height: 50,
            border: '1px solid var(--border)',
            borderRadius: 'var(--r-sm)',
            background: 'var(--surface)',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            overflow: 'hidden', flexShrink: 0,
          }}>
            {selected.has_image ? (
              <img
                src={VS.userStampImageUrl(selected.user_id)}
                alt=""
                style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain' }}
              />
            ) : (
              <span className="uppercase-label" style={{ color: 'var(--fg-subtle)', fontSize: 9 }}>brak</span>
            )}
          </div>
          <div style={{ fontSize: 11.5, lineHeight: 1.5 }}>
            <div style={{ fontWeight: 500, color: 'var(--fg)' }}>{selected.name || '—'}</div>
            <div className="mono" style={{ color: 'var(--fg-muted)', fontSize: 10.5 }}>
              {selected.cert_number ? `nr świadectwa: ${selected.cert_number}` : 'brak nr świadectwa'}
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

Object.assign(window, { OneTimeProtocolsScreen, OneTimeProtocolEditor });
