/* ============================================================
   ADMIN — Панель управления компаниями (доступы к сервису)
   Суперадмин заводит компанию по логину/паролю → компания
   с этими данными входит в свой личный кабинет (index.html).
   ============================================================ */

/* ---- Seed ------------------------------------------------- */
const TARIFFS = {
  start: { label: 'Старт',  cabs: 1 },
  biz:   { label: 'Бизнес', cabs: 3 },
  team:  { label: 'Команда', cabs: 5 },
};
const AV_TONES = [
  ['#1f7a4d', '#eaf4ee'], ['#3a5db0', '#e8f0f8'], ['#6a4d8f', '#f0ebf6'],
  ['#a07410', '#f7efda'], ['#b0473a', '#faecea'], ['#2f7d8f', '#e4f1f3'],
];
const initials = (s) => s.replace(/[«»"]/g, '').trim().split(/\s+/).slice(0, 2).map(w => w[0]).join('').toUpperCase();

let _seq = 100;
const uid = () => 'c' + Date.now().toString(36) + Math.random().toString(36).substring(2, 6);

/* ---- helpers ---------------------------------------------- */
// Заголовки авторизации админа для всех защищённых запросов /api.
// json=true добавляет Content-Type для тел в JSON.
const adminHeaders = (json = false) => {
  const h = { 'X-Admin-Token': localStorage.getItem('cp-admin-token') || '' };
  if (json) h['Content-Type'] = 'application/json';
  return h;
};

const STATUS = {
  active:  { pill: 'green',   dot: 'var(--green)', label: 'Активна' },
  blocked: { pill: 'red',     dot: 'var(--red)',   label: 'Заблокирована' },
  new:     { pill: 'amber',   dot: 'var(--amber)', label: 'Не входила' },
};
const PASS_WORDS = ['Pled', 'Run', 'Leaf', 'Drive', 'Baby', 'Eco', 'Read', 'Box', 'Wave', 'Sun', 'Pine', 'Stone'];
const genPass = () => {
  const w = PASS_WORDS[Math.floor(Math.random() * PASS_WORDS.length)];
  const n = Math.floor(1000 + Math.random() * 9000);
  const s = Math.random().toString(36).slice(2, 4);
  return `${w}-${n}-${s}`;
};
const slug = (s) => s.toLowerCase().replace(/[«»"]/g, '')
  .replace(/[^a-zа-я0-9\s-]/gi, '').trim().replace(/\s+/g, '-')
  .replace(/[а-я]/g, (c) => ({'а':'a','б':'b','в':'v','г':'g','д':'d','е':'e','ж':'zh','з':'z','и':'i','й':'y','к':'k','л':'l','м':'m','н':'n','о':'o','п':'p','р':'r','с':'s','т':'t','у':'u','ф':'f','х':'h','ц':'c','ч':'ch','ш':'sh','щ':'sch','ъ':'','ы':'y','ь':'','э':'e','ю':'yu','я':'ya'}[c] || c))
  .slice(0, 22);

/* ---- Company avatar --------------------------------------- */
function CoAvatar({ name, size = 38 }) {
  const idx = (name.charCodeAt(0) + name.length) % AV_TONES.length;
  const [ink, bg] = AV_TONES[idx];
  return (
    <span style={{ width: size, height: size, borderRadius: 10, background: bg, color: ink,
      display: 'grid', placeItems: 'center', fontWeight: 700, fontSize: size * 0.36, flex: 'none',
      letterSpacing: '-0.02em' }}>{initials(name)}</span>
  );
}

/* ---- Secret field (login / password reveal + copy) -------- */
function Secret({ value, mono = true, reveal, onToggle, onCopy, masked, field }) {
  const shown = !masked || reveal;
  return (
    <span className="secret">
      {field && <span className="secret-tag faint">{field}</span>}
      <span className={'secret-val' + (mono ? ' num' : '')}>{shown ? value : '••••••••'}</span>
      {masked && (
        <button className="secret-btn" title={reveal ? 'Скрыть' : 'Показать'} onClick={onToggle}>
          <Icon name="eye" size={15} />
        </button>
      )}
      <button className="secret-btn" title="Скопировать" onClick={onCopy}><Icon name="copy" size={15} /></button>
    </span>
  );
}

/* ---- Floating menu that clamps itself into the viewport --- */
function FloatingMenu({ top, left, onClose, children, className = 'menu' }) {
  const ref = useRef(null);
  React.useLayoutEffect(() => {
    const el = ref.current;
    if (!el) return;
    const r = el.getBoundingClientRect();
    const vw = document.documentElement.clientWidth;
    const vh = document.documentElement.clientHeight;
    let nl = left, nt = top;
    if (r.right > vw - 8) nl = vw - r.width - 8;
    if (nl < 8) nl = 8;
    if (r.bottom > vh - 8) nt = vh - r.height - 8;
    if (nt < 8) nt = 8;
    el.style.left = nl + 'px';
    el.style.top = nt + 'px';
  }, [top, left]);
  return (
    <>
      <div className="menu-scrim" onClick={onClose} />
      <div className={className} ref={ref} style={{ position: 'fixed', top, left }}>{children}</div>
    </>
  );
}

/* ---- Stat card -------------------------------------------- */
function Stat({ icon, label, value, tone, sub }) {
  return (
    <div className="card card-pad stat-card">
      <div className="stat-ico" style={tone ? { color: tone } : null}><Icon name={icon} size={19} /></div>
      <div className="num stat-num">{value}</div>
      <div className="stat-lab">{label}</div>
      {sub && <div className="faint" style={{ fontSize: 12, marginTop: 2 }}>{sub}</div>}
    </div>
  );
}

/* ============================================================
   Add / edit drawer
   ============================================================ */
function CompanyForm({ initial, onSave, onClose, existingLogins }) {
  const edit = !!initial;
  const [name, setName]       = useState(initial?.name || '');
  const [sphere, setSphere]   = useState(initial?.sphere || '');
  const [login, setLogin]     = useState(initial?.login || '');
  // При редактировании пароли не приходят с сервера (хранятся хешем). Пустое поле =
  // «не менять». При создании — генерируем стартовый пароль, видимый админу один раз.
  const [pass, setPass]       = useState(edit ? '' : genPass());
  const [opLogin, setOpLogin] = useState(initial?.op_login || '');
  const [opPass, setOpPass]   = useState(edit ? '' : genPass());
  const [tariff, setTariff]   = useState(initial?.tariff || 'biz');
  const [reveal, setReveal]   = useState(true);
  const [opReveal, setOpReveal] = useState(true);
  const [loginTouched, setLT] = useState(edit);
  const [opLoginTouched, setOpLT] = useState(edit);

  // auto-suggest logins from name until user edits the field
  useEffect(() => { if (!loginTouched) setLogin(slug(name)); }, [name, loginTouched]);
  useEffect(() => { if (!opLoginTouched) setOpLogin(name ? slug(name) + '-op' : ''); }, [name, opLoginTouched]);

  const others = existingLogins.filter(l => edit ? (l !== initial?.login && l !== initial?.op_login) : true);
  const clash = !!login && others.includes(login);
  const opClash = !!opLogin && (others.includes(opLogin) || opLogin === login);
  // На создании пароли обязательны; на редактировании — необязательны (пусто = без изменений).
  const valid = name.trim() && login.trim() && !clash
    && opLogin.trim() && !opClash
    && (edit || (pass.trim() && opPass.trim()));

  const submit = () => {
    if (!valid) return;
    onSave({
      ...(initial || {}),
      id: initial?.id || uid(),
      name: name.trim(), sphere: sphere.trim() || '—',
      login: login.trim(), pass: pass.trim(),
      op_login: opLogin.trim(), op_pass: opPass.trim(),
      tariff, cabs: TARIFFS[tariff].cabs,
      status: initial?.status || 'new',
      last: initial?.last ?? null,
      created: initial?.created || new Date().toLocaleDateString('ru-RU'),
    });
  };

  return (
    <Drawer open onClose={onClose} sub={edit ? 'Доступ к сервису' : 'Новая компания'}
      title={edit ? name || 'Компания' : 'Добавить компанию'}>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
        <p className="muted" style={{ fontSize: 13, lineHeight: 1.5, marginTop: -4 }}>
          Логин и пароль — это доступ компании в личный кабинет сервиса. Передайте их клиенту после создания.
        </p>

        <label className="fld">
          <span className="lab">Название компании</span>
          <input className="input" value={name} placeholder="Например, Дом и уют" autoFocus
            onChange={(e) => setName(e.target.value)} />
        </label>

        <label className="fld">
          <span className="lab">Сфера / товары <span className="faint" style={{ fontWeight: 500 }}>· необязательно</span></span>
          <input className="input" value={sphere} placeholder="Товары для дома"
            onChange={(e) => setSphere(e.target.value)} />
        </label>

        <hr className="hairline" />
        <div className="eyebrow" style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
          <Icon name="key" size={14} /> Данные для входа · Владелец
        </div>

        <label className="fld">
          <span className="lab">Логин</span>
          <input className="input mono" value={login} placeholder="dom-uyut"
            onChange={(e) => { setLT(true); setLogin(e.target.value.replace(/\s/g, '')); }} />
          {clash && <span className="field-err">Такой логин уже занят</span>}
        </label>

        <label className="fld">
          <span className="lab">Пароль</span>
          <div className="pass-row">
            <input className="input mono" type={reveal ? 'text' : 'password'} value={pass}
              placeholder={edit ? 'оставьте пустым — без изменений' : ''}
              onChange={(e) => setPass(e.target.value)} style={{ flex: 1 }} />
            <button className="iconbtn pass-ic" title={reveal ? 'Скрыть' : 'Показать'} onClick={() => setReveal(r => !r)}>
              <Icon name="eye" size={16} />
            </button>
            <button className="btn ghost" onClick={() => { setPass(genPass()); setReveal(true); }}>
              <Icon name="refresh" size={14} /> Сгенерировать
            </button>
          </div>
        </label>

        <hr className="hairline" />
        <div className="eyebrow" style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
          <Icon name="plug" size={14} /> Данные для входа · Оператор
        </div>

        <label className="fld">
          <span className="lab">Логин оператора</span>
          <input className="input mono" value={opLogin} placeholder="dom-uyut-op"
            onChange={(e) => { setOpLT(true); setOpLogin(e.target.value.replace(/\s/g, '')); }} />
          {opClash && <span className="field-err">
            {opLogin === login ? 'Совпадает с логином Владельца' : 'Такой логин уже занят'}
          </span>}
        </label>

        <label className="fld">
          <span className="lab">Пароль оператора</span>
          <div className="pass-row">
            <input className="input mono" type={opReveal ? 'text' : 'password'} value={opPass}
              placeholder={edit ? 'оставьте пустым — без изменений' : ''}
              onChange={(e) => setOpPass(e.target.value)} style={{ flex: 1 }} />
            <button className="iconbtn pass-ic" title={opReveal ? 'Скрыть' : 'Показать'} onClick={() => setOpReveal(r => !r)}>
              <Icon name="eye" size={16} />
            </button>
            <button className="btn ghost" onClick={() => { setOpPass(genPass()); setOpReveal(true); }}>
              <Icon name="refresh" size={14} /> Сгенерировать
            </button>
          </div>
        </label>

        <hr className="hairline" />
        <div className="fld">
          <span className="lab">Тариф</span>
          <div className="tariff-pick">
            {Object.entries(TARIFFS).map(([k, t]) => (
              <button key={k} className={'tariff-opt' + (tariff === k ? ' active' : '')} onClick={() => setTariff(k)}>
                <span className="t-name">{t.label}</span>
                <span className="t-meta faint">до {t.cabs} {t.cabs === 1 ? 'кабинета' : 'кабинетов'}</span>
              </button>
            ))}
          </div>
        </div>
      </div>

      <div className="drawer-foot">
        <button className="btn ghost" onClick={onClose}>Отмена</button>
        <button className="btn primary" disabled={!valid} style={!valid ? { opacity: .45, pointerEvents: 'none' } : null}
          onClick={submit}>
          <Icon name={edit ? 'check' : 'plus'} size={15} />
          {edit ? 'Сохранить' : 'Создать доступ'}
        </button>
      </div>
    </Drawer>
  );
}

/* ============================================================
   Companies screen
   ============================================================ */
function Companies({ list, fetchData, toast }) {
  const [q, setQ]         = useState('');
  const [filter, setFilter] = useState('all');
  const [revealed, setRevealed] = useState({});
  // Пароли компаний хранятся хешированными и по API не возвращаются. Здесь держим
  // ТОЛЬКО что сгенерированные при сбросе значения, чтобы показать их один раз.
  const [newPass, setNewPass] = useState({});
  const [form, setForm]   = useState(null); // null | {mode:'add'} | {mode:'edit', co}
  const [menu, setMenu]   = useState(null);
  const [accessMenu, setAccessMenu] = useState(null); // {id, top, left} — виджет "Доступы"
  const [justAdded, setJustAdded] = useState(null);

  const counts = {
    all: list.length,
    active: list.filter(c => c.status === 'active').length,
    blocked: list.filter(c => c.status === 'blocked').length,
    new: list.filter(c => c.status === 'new').length,
  };

  const view = list.filter(c => {
    if (filter !== 'all' && c.status !== filter) return false;
    if (!q.trim()) return true;
    const s = q.toLowerCase();
    return c.name.toLowerCase().includes(s) || c.login.toLowerCase().includes(s)
      || (c.op_login || '').toLowerCase().includes(s) || c.sphere.toLowerCase().includes(s);
  });

  const copy = (text, msg) => {
    try { navigator.clipboard?.writeText(text); } catch (e) {}
    toast(msg);
  };

  const save = async (co) => {
    const isEdit = list.some(x => x.id === co.id);
    const url = isEdit ? `/api/companies/${co.id}` : '/api/companies';
    const method = isEdit ? 'PUT' : 'POST';

    try {
      const res = await fetch(url, {
        method,
        headers: adminHeaders(true),
        body: JSON.stringify(co)
      });
      if (res.ok) {
        await fetchData();
        toast(isEdit ? 'Изменения сохранены' : 'Компания добавлена · доступ создан');
        setForm(null);
        if (!isEdit) {
          setJustAdded(co.id);
          setTimeout(() => setJustAdded(null), 2200);
        }
      } else {
        const err = await res.json();
        alert(err.error || 'Ошибка сохранения');
      }
    } catch (err) {
      console.error(err);
      toast('Ошибка соединения с сервером');
    }
  };

  const setStatus = async (id, status) => {
    try {
      const res = await fetch(`/api/companies/${id}/status`, {
        method: 'PUT',
        headers: adminHeaders(true),
        body: JSON.stringify({ status })
      });
      if (res.ok) {
        await fetchData();
        setMenu(null);
        toast(status === 'blocked' ? 'Доступ заблокирован' : 'Доступ разблокирован');
      }
    } catch (err) {
      console.error(err);
    }
  };

  const remove = async (id) => {
    try {
      const res = await fetch(`/api/companies/${id}`, {
        method: 'DELETE',
        headers: adminHeaders()
      });
      if (res.ok) {
        await fetchData();
        setMenu(null);
        toast('Компания удалена');
      }
    } catch (err) {
      console.error(err);
    }
  };

  const resetPass = async (id, role = 'owner') => {
    const np = genPass();
    try {
      const res = await fetch(`/api/companies/${id}/password`, {
        method: 'PUT',
        headers: adminHeaders(true),
        body: JSON.stringify({ pass: np, role })
      });
      if (res.ok) {
        await fetchData();
        // Новый пароль сервер хранит в виде хеша и обратно не отдаёт — показываем
        // сгенерированное значение один раз здесь и копируем в буфер обмена.
        setNewPass(p => ({ ...p, [id + (role === 'operator' ? 'op' : 'p')]: np }));
        try { navigator.clipboard?.writeText(np); } catch (e) {}
        setMenu(null);
        toast(`${role === 'operator' ? 'Пароль оператора' : 'Пароль Владельца'} обновлён и скопирован: ${np}`);
      }
    } catch (err) {
      console.error(err);
    }
  };

  const logEntrance = async (c, role = 'owner') => {
    try {
      await fetch('/api/access-log', {
        method: 'POST',
        headers: adminHeaders(true),
        body: JSON.stringify({
          company: c.name,
          login: role === 'operator' ? (c.op_login || c.login + '-op') : c.login,
          when_time: new Date().toLocaleDateString('ru-RU', {
            day: 'numeric',
            month: 'long',
            hour: '2-digit',
            minute: '2-digit'
          }).replace(' в ', ', '),
          ip: '192.168.1.55',
          ua: navigator.userAgent.includes('Macintosh') ? 'Safari · macOS' : 'Chrome · Windows',
          ok: 1,
          reason: null
        })
      });
      await fetchData();
    } catch (err) {
      console.error('Error logging entrance:', err);
    }
  };

  const FILTERS = [
    { id: 'all', label: 'Все', n: counts.all },
    { id: 'active', label: 'Активные', n: counts.active },
    { id: 'blocked', label: 'Заблок.', n: counts.blocked },
    { id: 'new', label: 'Новые', n: counts.new },
  ];

  return (
    <div>
      {/* stats */}
      <div className="stat-grid">
        <Stat icon="building" label="Всего компаний" value={counts.all} />
        <Stat icon="shield"   label="Активные"       value={counts.active} tone="var(--green)" />
        <Stat icon="ban"      label="Заблокированы"  value={counts.blocked} tone="var(--red)" />
        <Stat icon="clock"    label="Ещё не входили" value={counts.new} tone="var(--amber)" />
      </div>

      {/* toolbar */}
      <div className="toolbar">
        <div className="search">
          <Icon name="search" size={16} />
          <input value={q} onChange={(e) => setQ(e.target.value)} placeholder="Поиск по названию или логину" />
        </div>
        <div className="seg filter-seg">
          {FILTERS.map(f => (
            <button key={f.id} className={filter === f.id ? 'active' : ''} onClick={() => setFilter(f.id)}>
              {f.label} <span className="seg-n">{f.n}</span>
            </button>
          ))}
        </div>
        <button className="btn primary" onClick={() => setForm({ mode: 'add' })}>
          <Icon name="plus" size={15} /> Добавить компанию
        </button>
      </div>

      {/* table */}
      <div className="card co-card">
        <div className="table-scroll">
        <table className="co-table">
          <thead>
            <tr>
              <th>Компания</th>
              <th>Доступы</th>
              <th>Тариф</th>
              <th>Последний вход</th>
              <th>Статус</th>
              <th className="r"></th>
            </tr>
          </thead>
          <tbody>
            {view.map(c => {
              const st = STATUS[c.status];
              const blocked = c.status === 'blocked';
              return (
                <tr key={c.id} className={justAdded === c.id ? 'row-new' : ''}>
                  <td>
                    {blocked ? (
                      <div className="co-cell muted" style={{ opacity: 0.7 }}>
                        <CoAvatar name={c.name} />
                        <div style={{ minWidth: 0 }}>
                          <div className="co-name">{c.name}</div>
                          <div className="co-sphere faint">{c.sphere}</div>
                        </div>
                      </div>
                    ) : (
                      <a className="co-cell" href={`index.html?co=${c.id}&role=owner`} target="_blank" rel="noopener"
                        onClick={(e) => {
                          e.preventDefault();
                          window.open(`index.html?co=${c.id}&role=owner`, '_blank');
                          logEntrance(c, 'owner');
                        }}
                        style={{ textDecoration: 'none', color: 'inherit' }}
                        title="Открыть кабинет Владельца">
                        <CoAvatar name={c.name} />
                        <div style={{ minWidth: 0 }}>
                          <div className="co-name" style={{ color: 'var(--blue)' }}>{c.name}</div>
                          <div className="co-sphere faint">{c.sphere}</div>
                        </div>
                      </a>
                    )}
                  </td>
                  <td>
                    <button className="btn ghost access-btn" onClick={(e) => {
                      if (accessMenu && accessMenu.id === c.id) { setAccessMenu(null); return; }
                      const r = e.currentTarget.getBoundingClientRect();
                      setAccessMenu({ id: c.id, top: r.bottom + 6, left: r.left });
                    }}>
                      <Icon name="key" size={14} /> Доступы
                    </button>
                  </td>
                  <td>
                    <span className="pill neutral">{TARIFFS[c.tariff].label}</span>
                    <div className="faint" style={{ fontSize: 11.5, marginTop: 3 }}>{c.cabs} {c.cabs === 1 ? 'кабинет' : c.cabs < 5 ? 'кабинета' : 'кабинетов'}</div>
                  </td>
                  <td className="muted" style={{ fontSize: 13 }}>{c.last || <span className="faint">никогда</span>}</td>
                  <td>
                    <span className={'pill ' + st.pill}>
                      <span style={{ width: 6, height: 6, borderRadius: 9, background: st.dot }} />{st.label}
                    </span>
                  </td>
                  <td className="r">
                    <div className="row-actions">
                      <a className="iconbtn" href={`index.html?co=${c.id}&role=owner`} target="_blank" rel="noopener"
                        onClick={(e) => {
                          e.preventDefault();
                          window.open(`index.html?co=${c.id}&role=owner`, '_blank');
                          logEntrance(c, 'owner');
                        }}
                        title="Открыть кабинет Владельца" style={blocked ? { opacity: .4, pointerEvents: 'none' } : null}>
                        <Icon name="login" size={16} />
                      </a>
                      <a className="iconbtn" href={`index.html?co=${c.id}&role=operator`} target="_blank" rel="noopener"
                        onClick={(e) => {
                          e.preventDefault();
                          window.open(`index.html?co=${c.id}&role=operator`, '_blank');
                          logEntrance(c, 'operator');
                        }}
                        title="Открыть кабинет Оператора" style={blocked ? { opacity: .4, pointerEvents: 'none' } : null}>
                        <Icon name="plug" size={16} />
                      </a>
                      <button className="iconbtn" title="Ещё" onClick={(e) => {
                        if (menu && menu.id === c.id) { setMenu(null); return; }
                        const r = e.currentTarget.getBoundingClientRect();
                        setMenu({ id: c.id, top: r.bottom + 6, left: r.right - 212 });
                      }}>
                        <Icon name="more" size={16} />
                      </button>
                    </div>
                  </td>
                </tr>
              );
            })}
            {view.length === 0 && (
              <tr><td colSpan="6">
                <div className="empty">
                  <Icon name="search" size={22} />
                  <div>Ничего не найдено</div>
                  <button className="btn ghost" onClick={() => { setQ(''); setFilter('all'); }}>Сбросить фильтр</button>
                </div>
              </td></tr>
            )}
          </tbody>
        </table>
        </div>
      </div>

      {menu && (() => {
        const c = list.find(x => x.id === menu.id);
        if (!c) return null;
        const blocked = c.status === 'blocked';
        return (
          <FloatingMenu top={menu.top} left={menu.left} onClose={() => setMenu(null)}>
            <button onClick={() => { setForm({ mode: 'edit', co: c }); setMenu(null); }}>
              <Icon name="pencil" size={15} /> Редактировать
            </button>
            <button onClick={() => resetPass(c.id, 'owner')}>
              <Icon name="refresh" size={15} /> Сбросить пароль Владельца
            </button>
            <button onClick={() => resetPass(c.id, 'operator')}>
              <Icon name="refresh" size={15} /> Сбросить пароль Оператора
            </button>
            {blocked
              ? <button onClick={() => { setStatus(c.id, 'active'); setMenu(null); }}>
                  <Icon name="shield" size={15} /> Разблокировать
                </button>
              : <button onClick={() => { setStatus(c.id, 'blocked'); setMenu(null); }}>
                  <Icon name="ban" size={15} /> Заблокировать
                </button>}
            <hr className="hairline" style={{ margin: '4px 0' }} />
            <button className="danger" onClick={() => remove(c.id)}>
              <Icon name="trash" size={15} /> Удалить компанию
            </button>
          </FloatingMenu>
        );
      })()}

      {accessMenu && (() => {
        const c = list.find(x => x.id === accessMenu.id);
        if (!c) return null;
        const opLogin = c.op_login || `${c.login}-op`;
        const roles = [
          { key: 'p',  label: 'Владелец', login: c.login, role: 'owner' },
          { key: 'op', label: 'Оператор', login: opLogin, role: 'operator' },
        ];
        return (
          <FloatingMenu top={accessMenu.top} left={accessMenu.left} onClose={() => setAccessMenu(null)}
            className="menu access-menu">
            {roles.map((r, i) => {
              const fresh = newPass[c.id + r.key];
              return (
                <React.Fragment key={r.key}>
                  {i > 0 && <hr className="hairline" style={{ margin: '10px 0' }} />}
                  <div className="access-role-title">{r.label}</div>
                  <div className="access-row">
                    <Secret value={r.login} masked={false} field="логин"
                      onCopy={() => copy(r.login, 'Логин скопирован')} />
                  </div>
                  <div className="access-row">
                    {fresh ? (
                      // Только что сгенерированный пароль — показываем один раз.
                      <Secret value={fresh} masked
                        reveal={!!revealed[c.id + r.key]}
                        onToggle={() => setRevealed(rv => ({ ...rv, [c.id + r.key]: !rv[c.id + r.key] }))}
                        onCopy={() => copy(fresh, 'Пароль скопирован')} />
                    ) : (
                      // Хеш пароля недоступен для показа — только сброс на новый.
                      <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12.5 }}>
                        <span className="faint" style={{ flex: 1 }}>Пароль скрыт (хранится хешем)</span>
                        <button className="btn ghost" style={{ padding: '4px 10px', fontSize: 12 }}
                          onClick={() => resetPass(c.id, r.role)}>Сбросить</button>
                      </div>
                    )}
                  </div>
                </React.Fragment>
              );
            })}
          </FloatingMenu>
        );
      })()}

      {form && (
        <CompanyForm
          initial={form.co}
          existingLogins={list.flatMap(c => [c.login, c.op_login].filter(Boolean))}
          onSave={save}
          onClose={() => setForm(null)} />
      )}
    </div>
  );
}

/* ============================================================
   Access log screen
   ============================================================ */
function AccessLog({ log }) {
  return (
    <div>
      <p className="muted" style={{ fontSize: 13.5, marginBottom: 18, maxWidth: 620, lineHeight: 1.5 }}>
        Последние попытки входа компаний в личный кабинет по выданным логинам и паролям.
      </p>
      <div className="card co-card">
        <div className="table-scroll">
        <table className="co-table">
          <thead>
            <tr>
              <th>Компания</th><th>Логин</th><th>Время</th><th>IP</th><th>Устройство</th><th className="r">Результат</th>
            </tr>
          </thead>
          <tbody>
            {log.map((r, i) => (
              <tr key={i}>
                <td><span style={{ fontWeight: 500 }}>{r.company}</span></td>
                <td><span className="num" style={{ fontSize: 13 }}>{r.login}</span></td>
                <td className="muted" style={{ fontSize: 13 }}>{r.when_time}</td>
                <td><span className="num faint" style={{ fontSize: 12.5 }}>{r.ip}</span></td>
                <td className="muted" style={{ fontSize: 13 }}>{r.ua}</td>
                <td className="r">
                  {r.ok === 1 || r.ok === true
                    ? <span className="pill green"><Icon name="check" size={13} /> Успешно</span>
                    : <span className="pill red" title={r.reason}><Icon name="ban" size={13} /> {r.reason}</span>}
                </td>
              </tr>
            ))}
          </tbody>
        </table>
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   Admin shell
   ============================================================ */
const ADMIN_NAV = [
  { id: 'companies', label: 'Компании',       icon: 'building' },
  { id: 'log',       label: 'Журнал доступа', icon: 'clock' },
];
const ADMIN_TITLES = {
  companies: ['Компании', 'Доступы клиентов к сервису'],
  log:       ['Журнал доступа', 'Кто и когда заходил в кабинет'],
};

function AdminApp() {
  const [screen, setScreen] = useState('companies');
  const [theme, setTheme] = useState(() => {
    const saved = localStorage.getItem('cp-theme') || 'auto';
    const resolved = saved === 'auto'
      ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
      : saved;
    document.documentElement.setAttribute('data-theme', resolved);
    return saved;
  });
  // Вычисленная тема ('light'/'dark') — в режиме auto следует за системой
  const getResolved = (t) => {
    if (t !== 'auto') return t;
    return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
  };
  const [resolvedTheme, setResolvedTheme] = useState(() => getResolved(localStorage.getItem('cp-theme') || 'auto'));
  const [toasts, setToasts] = useState([]);
  
  const [list, setList] = useState([]);
  const [accessLog, setAccessLog] = useState([]);
  const [loading, setLoading] = useState(true);
  const [authed, setAuthed] = useState(null); // null = проверяем токен, true = доступ разрешён

  const fetchData = async () => {
    try {
      const res1 = await fetch('/api/companies', { headers: adminHeaders(), cache: 'no-store' });
      if (res1.ok) {
        const cos = await res1.json();
        setList(cos);
      }

      const res2 = await fetch('/api/access-log', { headers: adminHeaders(), cache: 'no-store' });
      if (res2.ok) {
        const logs = await res2.json();
        setAccessLog(logs);
      }
      setLoading(false);
    } catch (err) {
      console.error('Error fetching admin data:', err);
    }
  };

  // Гейт авторизации: без валидного токена админа — обратно на приветственную страницу.
  useEffect(() => {
    const token = localStorage.getItem('cp-admin-token');
    if (!token) { window.location.href = '/'; return; }
    let cancelled = false;
    (async () => {
      try {
        const res = await fetch('/api/admin-verify', { headers: { 'X-Admin-Token': token }, cache: 'no-store' });
        if (!res.ok) {
          localStorage.removeItem('cp-admin-token');
          window.location.href = '/';
          return;
        }
        if (!cancelled) setAuthed(true);
      } catch (err) {
        console.error('Admin auth check failed:', err);
        window.location.href = '/';
      }
    })();
    return () => { cancelled = true; };
  }, []);

  // Данные тянем только после подтверждённого доступа.
  useEffect(() => {
    if (authed !== true) return;
    fetchData();

    // Poll access logs every 5s for live login visual updates
    const logTimer = setInterval(async () => {
      try {
        const res = await fetch('/api/access-log', { headers: adminHeaders(), cache: 'no-store' });
        if (res.ok) {
          const logs = await res.json();
          setAccessLog(logs);
        }
      } catch (err) {
        console.error(err);
      }
    }, 5000);

    return () => clearInterval(logTimer);
  }, [authed]);

  const handleAdminLogout = async () => {
    const token = localStorage.getItem('cp-admin-token');
    try {
      await fetch('/api/admin-logout', { method: 'POST', headers: { 'X-Admin-Token': token || '' } });
    } catch (err) { /* всё равно выходим локально */ }
    localStorage.removeItem('cp-admin-token');
    window.location.href = '/';
  };

  useEffect(() => {
    localStorage.setItem('cp-theme', theme);
    if (theme === 'auto') {
      const mq = window.matchMedia('(prefers-color-scheme: dark)');
      const apply = (e) => {
        const resolved = e.matches ? 'dark' : 'light';
        document.documentElement.setAttribute('data-theme', resolved);
        setResolvedTheme(resolved);
      };
      apply(mq);
      mq.addEventListener('change', apply);
      return () => mq.removeEventListener('change', apply);
    }
    document.documentElement.setAttribute('data-theme', theme);
    setResolvedTheme(theme);
  }, [theme]);

  const toast = (msg) => {
    const id = Math.random().toString(36).slice(2);
    setToasts(t => [...t, { id, msg }]);
    setTimeout(() => setToasts(t => t.filter(x => x.id !== id)), 2600);
  };

  const [t0, t1] = ADMIN_TITLES[screen];

  if (authed !== true || loading) {
    return (
      <div style={{ display: 'grid', placeItems: 'center', height: '100vh', fontFamily: 'sans-serif', color: 'var(--ink-2)' }}>
        <div style={{ textAlign: 'center' }}>
          <div className="num" style={{ fontSize: 16, fontWeight: 600 }}>Загрузка панели администратора...</div>
        </div>
      </div>
    );
  }

  return (
    <div className="app" data-theme={resolvedTheme}>
      <aside className="sidebar">
        <div className="brand">
          <div className="brand-mark">ПУ</div>
          <div>
            <div className="brand-name">Панель управления</div>
            <div className="brand-sub">администратор сервиса</div>
          </div>
        </div>

        <div className="store-pick" style={{ cursor: 'default' }}>
          <span className="store-dot" />
          <div style={{ flex: 1 }}>
            <div className="nm">Все компании</div>
            <div className="meta">бизнес на маркетплейсах</div>
          </div>
          <span className="chev"><Icon name="users" size={16} /></span>
        </div>

        <div className="nav-label">Адменистрирование</div>
        <nav style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
          {ADMIN_NAV.map(n => (
            <button key={n.id} className={'nav-item' + (screen === n.id ? ' active' : '')} onClick={() => setScreen(n.id)}>
              <span className="nav-ico"><Icon name={n.icon} size={18} /></span>{n.label}
            </button>
          ))}
        </nav>

      </aside>

      <header className="topbar">
        <div>
          <h1>{t0}</h1>
          <div className="crumb">{t1}</div>
        </div>
        <div className="topbar-right">
          <span className="today">5 июня 2026</span>
          <button className="theme-btn" onClick={() => setTheme(resolvedTheme === 'dark' ? 'light' : 'dark')}
            title={resolvedTheme === 'dark' ? 'Светлая тема' : 'Тёмная тема'}>
            <Icon name={resolvedTheme === 'dark' ? 'sun' : 'moon'} size={17} />
          </button>
          <button className="btn ghost" onClick={handleAdminLogout} title="Выйти из панели администратора"
            style={{ padding: '8px 14px', fontSize: 12.5 }}>
            <Icon name="login" size={15} /> Выйти
          </button>
          <div className="av">А</div>
        </div>
      </header>

      <main className="main">
        <div className="content">
          <div className="content-wrap">
            <div className="screen-anim" key={screen}>
              {screen === 'companies' && <Companies list={list} fetchData={fetchData} toast={toast} />}
              {screen === 'log' && <AccessLog log={accessLog} />}
            </div>
          </div>
        </div>
      </main>

      <div className="toast-host">
        {toasts.map(t => (
          <div key={t.id} className="toast"><Icon name="check" size={15} /> {t.msg}</div>
        ))}
      </div>
    </div>
  );
}

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